使用同步机制的这种方式解决线程安全问题,锁对象在哪里释放锁.
Lock锁:
Lock实现提供一个更具体的锁对象:Lock
Lock实现提供了更广泛的锁定操作
Lock是一个接口, ReentrantLock子实现类 lock()获取锁,unlock()试图释放此锁
public class SellTicket implements Runnable {
private 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();
}
}
}
}
Try…finally(里面unlock());
使用同步机制解决了多线程的安全问题:但是存在弊端.
1. 同步导致执行效率(加锁解锁)
2. 容易出现死锁现象
当有两个锁同时加时:会导致死锁:
Eg:
public void run() {
private boolean flag ;
publicDieLock(boolean flag){
this.flag= flag ;
}
if(flag){
synchronized(MyLock.objA){
System.out.println("ifobjA");
synchronized(MyLock.objB) {
System.out.println("ifobjB");
}
}//代码执行完毕,objA锁相当于才能被释放掉
}else{
//dl2
synchronized(MyLock.objB) {
System.out.println("elseobjB");
synchronized(MyLock.objA){
System.out.println("elseobjA");
}
}
}
一个锁无法释放:线程执行随机性:
导致死锁现象
死锁线程:两个或者两个以上的线程出现了互相等待的情况,就会出现死锁!
消费者-生产者模式:
建立学生类:Student类.
生产者模式:SetThread类:
消费者模式:GetThread类:
测试类:StudentDemo,实现多线程环境
学生类:
public class Student {
private String name;
private int age;
}
生产者模式:
public class SetThread implements Runnable {
private Student s;
public SetThread(Student s){
this.s=s;
}
@Override
public void run(){
s.name = "龙哥" ;
s.age = 20;
}
}
Run()方法里面设置名字和年龄.
消费者模式:
public class GetThread implements Runnable{
private Student s;
public GetThread(Student s){
this.s=s;
}
@Override
public void run() {
System.out.println(s.name+"--"+s.age);
}
}
Run()方法获取姓名和年龄.
对于每一个线程都在创建自己的学生对象,两个线程操作的两个对象而不是同一个对象,所以应该解决:
将学生对象成员变量,然后通过构造方法进行传递,在测试类中,创建学生对象(同一个资源对象)让多个线程对这个学生对象进行操作
为了数据多并且效果更明显,加入循环语句进行操作,给生产者线程和消费者线程分别加入循环语句(while循环)
改进后的生产者消费者模式:
生产者模式:
public class SetThread implements Runnable{
private Student s;
private int x=0;
public SetThread(Student s){
this.s=s;
}
@Override
public void run() {
while(true){
synchronized(s){
if(x%2==0){
s.name="UZI";
s.age=21;
}else{
s.name="Faker";
s.age=22;
}
x++;
}
}
}
}
消费者模式:
public class GetThread implements Runnable{
private Students;
public GetThread(Student s){
this.s=s;
}
@Override
public void run(){
while(true){
synchronized(s){
System.out.println(s.name+"--"+s.age);
}
}
}
}
输出的数据为 null-0时 在set ()get()里面创建学生对象
将学生对象成员变量,然后通过构造方法进行传递,
在测试类中,创建学生对象(同一个资源对象)让多个线程对这个学生对象进行操作
改进后的情况:
同一个数据输出多次
且姓名年龄不符合:线程的随机性导致
当前多线程有安全问题:
多线程安全问题的标准:
1)是否是多线程环境
2)是否有共享数据
3)是否有多条语句对共享数据操作
再改建:使用synchronized 同步锁对象
保证线程的安全性
新的问题:数据打印一次打印好多.
需求:一次输出一条,轮换输出:
利用等待唤醒机制.
改进:
Student类:
public class Student {
Stringname;
intage ;
booleanflag ; //默认false,true有数据
生产者模式里面:
While(true)
在同步锁对象里面进行线程等待
调用 s.wait():
while(true){
synchronized(s) {
if(s.flag){
try{
s.wait();//使线程等待
}catch (InterruptedException e) {
e.printStackTrace();
}
}
s.flag = true;// 修改:flag:
s.notify() ;// 下来唤醒线程状态:
消费者模式里面:
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 ;//消费者线程 .修改为false
s.notify();//唤醒t1线程....
}
生产者-消费者模式
最终的改进:
将资源对象Student的成员变量私有化
给类中提供两个方法
在两个线程中调用方法.
学生类:
public class Student {
private String name;
private int age;
private boolean flag;
//生产者模式的方法
public synchronized void set(String name,int age){
if(this.flag){
try{
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
this.name = name ;
this.age = age ;//产生数据
this.flag=true;//有数据了,可以使用get()
this.notify();//唤醒状态
}
//消费者模式的放法
public synchronized void get(){
if(!this.flag){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(this.name+"-"+this.age);
//线程t1和t2分别调用Set() 和Get()
this.flag=false;//没有数据了 ,需要生产数据通知线程t1,生产线程
this.notify();//唤醒t1线程
}
}
生产者模式:
public class SetThread implements Runnable{
private Student s;
public SetThread(Student s){
this.s=s;
}
private int x=0;
@Override
public void run(){
while(true){
if(x%2==0){
s.set("UZI", 20);
}else{
s.set("Faker", 21);
}
x++;
}
}
}
消费者模式:
public class GetThread implements Runnable{
private Student s;
public GetThread(Students){
this.s=s;
}
@Override
public void run(){
while(true){
s.get();
}
}
}
测试类:
public class StudentDemo {
public static void main(String[] args) {
Student s = new Student();
SetThread st = new SetThread(s);
GetThread gt = new GetThread(s);
Thread s1 = new Thread(st);
Thread s2 = new Thread(gt);
s1.start();
s2.start();
}
}
线程组:
MyRunnable:
public class MyRunnable implements Runnable{
@Override
public void run(){
for(int x=0;x<100;x++){
System.out.println(Thread.currentThread().getName()+"-"+x);
}
}
}
测试类:
public class ThreadGroupDemo {
public static void main(String[] args) {
method();//设置线程名称
}
private static void method() {
//设置线程组名称
ThreadGroup tg = new ThreadGroup("LONG");
//创建资源对象
MyRunnable mr = new MyRunnable();
Thread s1 = new Thread(tg,mr,"first");
Thread s2 = new Thread(tg,mr,"second");
//获取线程组名称的第一种方法
System.out.println(s1.getThreadGroup().getName());
System.out.println(s2.getThreadGroup().getName());
//获取线程组名称的第二种方法
ThreadGroup tg1=s1.getThreadGroup();
ThreadGroup tg2=s2.getThreadGroup();
System.out.println(tg1.getName());
System.out.println(tg2.getName());
tg.setDaemon(true);
s1.start();
s2.start();
}
}
线程池:
节约成本:子线程调用完后,不会立即收到,回到线程池多次利用
增加Executors工厂类产生线程池:
//public static ExecutorServicenewFixedThreadPool(int nThreads)
Executors工厂类中的这个方法参数直接指定在当前线程池中有多少个线程
int nThreads 为数字:指定线程个数.
方法返回值为ExecutorService:表示一个线程池,可以执行Runnable对象或者Callable对象代表的线程.
ExecutorService:接口中的方法
Future<?> submit(Runnable task)
测试类:
public class ExcutorsDemo {
public static void main(String[] args) {
//创建线程池对象,使用Executors工厂类
ExecutorService pool = Executors.newFixedThreadPool(2);
//提交多个任务
pool.submit(new MyRunnable());
pool.submit(new MyRunnable());
//结束线程池
pool.shutdown();
}
}
多线程实现方式:第三种:实际用到少
public class MyCallable implements Callable<Object> {
@Override
public Object call() throws Exception {
for(int x=0;x<100;x++){
System.out.println(Thread.currentThread().getName()+"-"+x);
}
return null;
}
}
测试类:与线程池一直:
1. 创建线程池对象
2. 提交Callable任务:eg:Threadpool.submit(new MyCallable()) ;
相当于线程中start()方法。
3.结束线程池
JavaSe中的计时器:
常用方法:
Public void schedule(TimerTask task,Datetime)安排在指定的时间执行任务
Public void schedule(TimerTask task,longdelay)在多少毫秒后执行指定任务
Public void schedule(TimerTask task,longdelay,long_period)
在多少毫秒后执行任务,且每多少毫秒重复执行
Public void cancel()终止此计时器,舍弃所有当前已安排的任务
一:定义一个任务类: 创建一个计时器,任务在多长时间后执行。之后结束任务
public class TimerDemo {
public static void main(String[] args) {
Timert = new Timer();
t.schedule(new MyTask(t), 5000);
}
}
//一个任务类:MyTask
class MyTask extends TimerTask{
private Timert;
public MyTask(){
}
public MyTask(Timer t){
this.t=t;
}
@Override
public void run() {
System.out.println("任务结束.");
t.cancel();//终止t计时器,舍弃任务
}
}
二:一个任务类:在多少毫秒后执行,且没多少毫秒重复执行
import java.util.Timer;
import java.util.TimerTask;
public class TimerDemo2 {
public static void main(String[] args) {
Timer t = new Timer();
t.schedule(new MyStack2(),3000 , 2000);
}
}
class MyStack2 extends TimerTask{
@Override
public void run() {
System.out.println("两秒后重复执行");
}
}
在指定时间递归删除目录:
public class DeleteFolder extends TimerTask {
@Override
public void run() {
// 封装当前项目下的demo文件
File srcFolder = new File("Demo");
deleteFolder(srcFolder);
}
private void deleteFolder(File srcFolder) {
//获取当前srcFolder下的所有文件以及File数组
File[] FileArray = srcFolder.listFiles();
if(FileArray!=null){
for(File file:FileArray){
//判断是否为文件夹
if(file.isDirectory()){
deleteFolder(file);
}else{//文件直接删除
System.out.println(file.getName()+"--"+file.delete());
}
}
System.out.println(srcFolder.getName()+"--"+srcFolder.delete());
}
}
public static class TimerTest {
public static void main(String[] args) throws ParseException {
Timer t = new Timer() ;
String dateStr = "2017-12-714:49:00" ;
//创建SimpleDateFormat对象
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss") ;
Date date = sdf.parse(dateStr) ;
t.schedule(new DeleteFolder(), date) ;
}
}
}
多线程的匿名内部类:
格式:
new 类名(具体类,抽象类),接口(){
重写/实现方法;
}
Eg:
继承自Thread类
new Thread(){
@Override
public void run() {
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() ;