您的位置:首页 > 其它

Lock-线程组-线程池-线程状态的转换图

2017-12-08 16:42 246 查看
一.Lock(接口)

1.使用同步机制的方式解决线程安全问题,但是不知道具体的锁对象在哪里添加,并且锁对象在哪里释放锁对象,对于这种情况,jdk5以后java提供了一个更具体的锁对象

Lock 实现提供了比使用 synchronized 方法和语句可获得的更广泛的锁定操作


Lock是一个接口,所以它在使用时用
ReentrantLock子实现类


2.加锁和释放锁的方法:

public void lock()获取锁


public void unlock()试图释放此锁


try{

锁对象.lock()

..........

}finally{

锁对象.unlock()
//释放资源


}

package lock;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class SellTickets implements Runnable{
private static int tickets = 100;
private Lock lock = new ReentrantLock();
@Override
public void run() {
// 模拟电影院售票,假设一直有票
while (true){
//加锁
try{
lock.lock();

if(tickets>0){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"正在出售第"+tickets--+"张票");
}
//释放锁
}finally{
lock.unlock();
}
}
}
}
package lock;

public class SellTicketsDemo {

public static void main(String[] args) {
// 创建SynRunnable对象
SellTickets st = new SellTickets();

//创建线程类对象
Thread t1 = new Thread(st,"窗口1");
Thread t2 = new Thread(st,"窗口2");
Thread t3 = new Thread(st,"窗口3");

//开启线程
t1.start();
t2.start();
t3.start();
}
}
3.同步机制的优缺点
优点:解决多线程的安全问题
缺点:1)同步,执行效率低(每个线程在抢占到CPU的执行权后,会将门关闭,其他线程不能进来)

  2)容易出现死锁:两个或者两个以上的线程出现了互相等待的情况,容易死锁
4.线程间的通信:生产者消费者模式(多个线程对同一资源进行操作)

例子: 1)资源对象:Student类
  提供一些成员变量:姓名 和年龄

2)生产者线程:SetThread类:  生产一些学生数据(设置学生数据)

    3)消费者线程:GetThread类:  输出学生数据

 
 4)测试类:StudentDemo类,实现多线程环境

对于生产者来说,产生数据,如果处于等待(wait)状态就说名有数据,通知消费者有线程,赶紧输出数据

对于消费者线程,输出数据,如果没有数据,应该通知生产者产生数据。

    以上是一个多线程环境,会出现线程不安全

1)同一数据打印一片(CPU的一点点时间片足够执行很多此)

2)年龄和姓名不符合(线程的随机性导致的)

使用等待唤醒机制可以解决问题1);使用同步机制可以解决问题2)

package waitnotify;

public class Student {
String name;
int age;

//声明一个变量
boolean flag;//默认没有数据,如果是true,则说明有数据
}
package waitnotify;
//生产者线程
public class SetThread implements Runnable{

private Student s;
public SetThread(Student s){
this.s = s;
}
private int i = 0;
@Override
public void run() {
while(true){
//同步机制可以解决数据不一致
synchronized(s){
//等待唤醒机制
//判断有没有数据
if(s.flag){  //对于生产者来说,没有数据,则处于等待状态,
//处于等待状态
try {
s.wait();//阻塞方式
} catch (InterruptedException e) {
e.printStackTrace();
}
}

if(i%2==0){
//设置学生数据
s.name = "韩庚";
s.age = 18;
}else{
//设置学生数据
s.name = "张靓颖";
s.age = 16;
}
i++;

//修改标记
s.flag = true;//有数据
//通知gt:消费者线程来消费
s.notify();//唤醒等待状态
}

}

}

}
package waitnotify;

public class GetThread implements Runnable{
private Student s;
public GetThread(Student s){
this.s = s;
}

@Override
public void run() {
//使用同步机制可以解决数据不一致现象
while(true){
synchronized(s){

//使用等待唤醒机制
if(!s.flag){
try {
s.wait();//调用的时候会立即释放锁
} catch (InterruptedException e) {

e.printStackTrace();
}
}

//输出语句
System.out.println(s.name+"----"+s.age);

//修改标记
s.flag = false;//消费者线程没有可输出的数据

//通知对方st线程,消费者线程没有数据,赶紧产生数据
s.notify();		//唤醒st线程

}
}
}
}
package waitnotify;

public class StudentDemo {

public static void main(String[] args) {
// 创建资源对象
Student s = new Student();

//创建资源对象
SetThread st = new SetThread(s);
GetThread gt = new GetThread(s);

//创建线程类对象
Thread t1 = new Thread(st);
Thread t2 = new Thread(gt);

//分别启动线程
t1.start();
t2.start();
}
}

以上例子的成员变量不是私有化,一般成员变量私有化,可以对上面的进行改进

package lastwaitnotify;

public class Student {
private String name;
private int age;
//声明一个变量
private boolean flag; //默认没有数据,如果是true,则说明有数据

//set(String name,int age)方法,产生数据

//同步机制可以解决数据不一致
public synchronized void set(String name,int age){
//等待唤醒机制
//判断有没有数据
if(this.flag){ //对于生产者来说,没有数据,则处于等待状态,
//处于等待状态
try {
this.wait();//阻塞方式,立即释放锁 notify
} catch (InterruptedException e) {
e.printStackTrace();
}
}

//设置学生数据
this.name = name;
this.age = age;

//修改标记
this.flag = true;//有数据
//通知gt:消费者线程来消费
this.notify();//唤醒等待状态
}

//get()输出数据
public synchronized void get(){
if(!this.flag){
try {
this.wait() ;//调用的时候,会立即释放锁
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//输出语句
System.out.println(this.name+"---"+this.age);

//修改标记
this.flag = false ;//消费者线程
//通知对方(st线程),消费者线程没有数据类,赶紧来消费
this.notify() ;//唤醒t1线程....
}

}
package lastwaitnotify;
//生产者线程
public class SetThread implements Runnable{

private Student s;
public SetThread(Student s){
this.s = s;
}
private int i = 0;
@Override
public  void run() {
while(true){
if(i%2==0){
//设置学生数据
s.set("韩庚", 18);
}else{
//设置学生数据
s.set("张靓颖", 16);
}
i++;
}
}
}
package lastwaitnotify;

public class GetThread implements Runnable{
private Student s;
public GetThread(Student s){
this.s = s;
}

@Override
public void run() {

while(true){
s.get();
}
}
}
package lastwaitnotify;

public class StudentDemo {

public static void main(String[] args) {
// 创建资源对象
Student s = new Student();

//创建资源对象
SetThread st = new SetThread(s);
GetThread gt = new GetThread(s);

//创建线程类对象
Thread t1 = new Thread(st);
Thread t2 = new Thre
cbf0
ad(gt);

//分别启动线程
t1.start();
t2.start();

}

}

二.线程组(ThreadGroup)
表示一个线程的集合
1.public final ThreadGroup getThreadGroup()返回该线程的线程组

2.public ThreadGroup(String name)设置线程组名称

3.public Thread(ThreadGroup,Runnable target,String name)给每一个子线程设置线程组名称
package threadgroup;

public class ThreadGroupDemo {
public static void main(String[] args) {
//线程组:一个线程的集合
method1();
method2();
}
//给每一个子线程设置线程名称
private static void method2() {

//public ThreadGroup(String name)构造一个新线程组

ThreadGroup tg = new ThreadGroup("这是一个新的线程组") ;

//创建资源对象
MyRunnable t1 = new MyRunnable();

//创建线程类对象,并且将线程组对象作为参数进行传递,就使用Thread类的构造方法
//public Thread(ThreadGroup group,Runnable target ,String name){}
Thread th1 = new Thread(tg, t1, "线程1") ;
Thread th2 = new Thread(tg, t1, "线程2") ;

//public final ThreadGroup getThreadGroup()返回该线程的线程组
ThreadGroup tg1 = th1.getThreadGroup() ;
ThreadGroup tg2 = th2.getThreadGroup() ;

System.out.println(tg1.getName());
System.out.println(tg2.getName());

tg.setDaemon(true) ;//将线程组中的所有的线程都设置为守护线程(后台线程)

}

private static void method1() {
// 获取多个线程所在的线程组名称

//创建资源对象
MyRunnable tg = new MyRunnable();

//创建线程类对象
Thread t1 = new Thread(tg);
Thread t2 = new Thread(tg);

//获取t1和t2所在的线程名称
//获取线程组对象
//public final ThreadGroup getThreadGroup()返回该线程所属的线程组
ThreadGroup tg1 = t1.getThreadGroup();
ThreadGroup tg2 = t2.getThreadGroup();

String name1 = tg1.getName();
String name2 = tg2.getName();

//子线程默认的线程组名称:main线程
System.out.println(name1);//main
System.out.println(name2);//main

//所有的线程它的默认线程组名称就是main
System.out.println(Thread.currentThread().getThreadGroup().getName());
}

}
package threadgroup;

public class MyRunnable implements Runnable {

@Override
public void run() {
while(true){
for(int i=0;i<100;i++){
System.out.println(Thread.currentThread().getName()+"---"+i);
}
}
}
}
三.线程池
线程池的好处:节约成本,很多子线程调用完毕不会立即被回收掉,而是会回到线程池中被多次利用!
JDK5新增了一个Executors工厂类来产生线程池,有如下几个方法
1)public static ExecutorService
newFixedThreadPool(int nThreads)

Executors工厂类中的这个方法参数直接指定在当前线程池中有多少个线程
这些方法的返回值是ExecutorService对象,该对象表示一个线程池,可以执行Runnable对象或者Callable对象代表的线程。

ExecutorsService :接口中的方法

2)Future<?>
submit(Runnable task)

<T> Future<T> submit(Callable<T> task)package threadpool;

public class MyRunnable implements Runnable {

@Override
public void run() {
while(true){
for(int i=0;i<100;i++){
System.out.println(Thread.currentThread().getName()+":"+i);
}
}

}

}
package threadpool;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ExecutorDemo {
public static void main(String[] args) {
//创建线程池对象
//public static ExecutorService newFixedThreadPool(int nThreads)
ExecutorService pool = Executors.newFixedThreadPool(2);

//使用submit(Runnable target):提交多个任务
pool.submit(new MyRunnable());
pool.submit(new MyRunnable());

//结束线程池
pool.shutdown();

}
}
多线程实现的方式三(实际开发中很少用)
创建线程池对象 实现callable接口

package callable;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
* 多线程程序的实现方式3:(实际开发中很少用到!)
* public static ExecutorService newFixedThreadPool(int nThreads)
Executors工厂类中的这个方法参数直接指定在当前线程池中有多少个线程
这些方法的返回值是ExecutorService对象,该对象表示一个线程池,可以执行Runnabl e对象或者Callable对象代表的线程。它提供了如下方法

ExecutorsService :接口中的方法
<T> Future<T> submit(Callable<T> task)
* @author Apple
*/
public class CallableDemo {

public static void main(String[] args) {
// 创建线程池对象
ExecutorService threadpool = Executors.newFixedThreadPool(2);

//提交Callable任务
threadpool.submit(new MyCallable());
threadpool.submit(new MyCallable());
//结束线程池
threadpool.shutdown();

}

}
package callable;

import java.util.concurrent.Callable;

public class MyCallable implements Callable<Object> {

@Override
public Object call() throws Exception {
for(int i=0;i<100;i++){
System.out.println(Thread.currentThread().getName()+":"+i);
}
return null;
}

}
四.多线程中的匿名内部类的方式
格式:new 类名(具体类,抽象类),接口(){

  重写/实现方法;

   }


匿名内部类的本质:继承了该类或者是实现该接口的子类对象!
public class ThreadDemo {
public static void main(String[] args) {

//继承自Thread类
new Thread(){
@Override
public void run() {
//for循环
for(int x = 0 ; x <100 ; x ++){
System.out.println(Thread.currentThread().getName()+":"+x);
}
}
}.start() ;//启动线程

//Runnable接口的方式
new Thread(new Runnable() {

@Override
public void run() {
//for循环
for(int x = 0 ; x < 100 ; x ++){
System.out.println(Thread.currentThread().getName()+":"+x);
}
}
}).start() ;
}五.定时器Timer
常用的方法:

public void schedule(TimerTask task,Date time)安排在指定的时间执行指定的任务


public void schedule(TimerTask task, long delay)在多少毫秒后执行指定任务

public void schedule(TimerTask task, long delay, long period)在多少毫秒后,执行任务,并且每个多少毫秒重复执行

public void cancel()终止此计时器,丢弃所有当前已安排的任务  

TimeTask是一个抽象类,自定义一个类实现它

练习:在指定的时间内删除当前项目下Demo文件夹中的内容

package timer;

import java.io.File;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;

public class TimerTest {

public static void main(String[] args) throws ParseException {
//创建Timer定时器
Timer t = new Timer();

//定义一个文本日期格式
String dateStr = "2017-12-8 16:50:00";
//解析成Date对象
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
//解析方法
Date date = sdf.parse(dateStr);

//public void schedule(TimerTask task,Date time)安排在指定的时间执行指定的任务
//调用定时器在指定的时间内执行某个任务
t.schedule(new DeleteFolder(), date);

}

}
//删除Demo文件夹
class DeleteFolder extends TimerTask{

@Override
public void run() {
// 封装当前项目下的Demo文件
File srcFolder = new File("demo");
deleteFolder(srcFolder);
}

//递归删除
private void deleteFolder(File srcFolder) {
// 获取当前strFolder下面的所以文件及文件夹
File[] fileArray = srcFolder.listFiles();
//对该对象非空判断
if(fileArray!=null){
//增强for遍历
for(File file : fileArray){
//判断file对象是否是文件夹
if(file.isDirectory()){
//继续回到删除目录的方法
deleteFolder(file);
}else{
//不是目录,是文件直接删除
System.out.println(file.getName()+"---"+file.delete());
}
}
System.out.println(srcFolder.getName()+"---"+srcFolder.delete());
}

}

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐