为了更清晰的表达如何加锁和释放锁,JDK1.5以后提供了一个新的锁对象Lock

Lock:
void lock() :获取锁
void unlock() :释放锁
ReentrantLock是Lock的实现类

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();            }     } }

}

public class SellTicketDemo {   public static void main(String[] args) {      // 创建资源对象     SellTicket st = new SellTicket();

      // 创建三个窗口     Thread t1 = new Thread(st, "窗口1");     Thread t2 = new Thread(st, "窗口2");     Thread t3 = new Thread(st, "窗口3");

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

同步的弊端: A:效率低 B:容易产生死锁

死锁: 两个或两个以上的线程在争夺资源的过程中,发生的一种相互等待现象。

public class MyLock {    // 创建两把锁对象    public static final Object objA = new Object();  public static final Object objB = new Object();}

public class DieLock extends Thread {

  private boolean flag;

   public DieLock(boolean flag) {        this.flag = flag;    }

   @Override    public void run() {       if (flag) {           synchronized (MyLock.objA) {              System.out.println("if objA");              synchronized (MyLock.objB) {                  System.out.println("if objB");              }         }     } else {          synchronized (MyLock.objB) {              System.out.println("else objB");                synchronized (MyLock.objA) {                  System.out.println("else objA");                }         }     } }}

public class DieLockDemo {  public static void main(String[] args) {      DieLock dl1 = new DieLock(true);     DieLock dl2 = new DieLock(false);

      dl1.start();      dl2.start();  }}

线程间通信: 生产者和消费者模式

资源类 :Student
设置学生数据: SetThread(生产者)
获取学生数据: GetThread(消费者)
测试类: StudentDemo

版本一:

public class Student {   String name;  int age;}

public class GetThread implements Runnable {   private Student s;

  public GetThread(Student s) {     this.s = s;  }

   @Override    public void run() {       // Student s = new Student();        System.out.println(s.name + "---" + s.age);   }

}

public class SetThread implements Runnable {

  private Student s;

  public SetThread(Student s) {     this.s = s;  }

   @Override    public void run() {       // Student s = new Student();        s.name = "林青霞";        s.age = 27;  }

}

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();   }}

修改版本一:为了数据的效果好一些,我加入了循环和判断,给出不同的值,这个时候产生了新的问题
A: 同一个数据出现多次
B:姓名和年龄不匹配
原因: A:同一个数据出现多欠: CPU的一点点时间片的执行权,就足够它执行多次
B:姓名和年龄不匹配: 线程运行的随机性

线程安全问题:
A: 是否是多线程环境 B: 是否共享数据 C: 是否有多条语句操作共享数据
解决方案:
加锁;
注意: A:不同种类的线程都要加锁。 B:不同种类的线程加的锁必须是同一把

加锁后的版本:

public class GetThread implements Runnable { private Student s;

  public GetThread(Student s) {     this.s = s;  }

   @Override    public void run() {       while (true) {            synchronized (s) {                System.out.println(s.name + "---" + s.age);           }     } }}

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 = "林青霞";//刚走到这里,就被别人抢到了执行权                   s.age = 27;              } else {                  s.name = "刘意"; //刚走到这里,就被别人抢到了执行权                   s.age = 30;              }             x++;            }     } }}

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();   }}

问题3:虽然数据安全了,但是呢,一次一大片不好看,我就想依次的一次一个输出。
如何实现呢?
通过Java提供的等待唤醒机制解决。

等待唤醒:
Object类中提供了三个方法:
wait() : 等待
notify() : 唤醒单个线程
notifyAll() : 唤醒所有线程

为什么不定义在Thread类中?
这些方法的调用必须通过锁对象调用,而代码块中的锁对象是任意对象,
所以这些方法必须定义在Object类中。

加了等待唤醒后的版本:

public class Student {   String name;  int age;  boolean flag; // 默认情况是没有数据,如果是true,说明有数据}

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(); //t2就等待了。立即释放锁。将来醒过来的时候,是从这里醒过来的时候                   } catch (InterruptedException e) {                        e.printStackTrace();                  }             }

               System.out.println(s.name + "---" + s.age);               //林青霞---27                //刘意---30

               //修改标记                s.flag = false;              //唤醒线程                s.notify(); //唤醒t1            }     } }}

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(s.flag){                   try {                     s.wait(); //t1等着,释放锁                   } catch (InterruptedException e) {                        e.printStackTrace();                  }             }

               if (x % 2 == 0) {                   s.name = "林青霞";                    s.age = 27;              } else {                  s.name = "刘意";                 s.age = 30;              }             x++; //x=1

               //修改标记                s.flag = true;               //唤醒线程                s.notify(); //唤醒t2,唤醒并不表示你立马可以执行,必须还得抢CPU的执行权。         }         //t1有,或者t2有        } }}

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();   }}

线程组 ThreadGroup
线程组: 把多个 线程组合到一起。
它可以对一批线程进行分类管理,Java允许程序直接对线程组进行控制。

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) {      // method1();

       // 我们如何修改线程所在的组呢?     // 创建一个线程组        // 创建其他线程的时候,把其他线程的组指定为我们自己新建线程组       method2();

      // t1.start();        // t2.start();    }

   private static void method2() {       // ThreadGroup(String name)       ThreadGroup tg = new ThreadGroup("这是一个新的组");

     MyRunnable my = new MyRunnable();        // Thread(ThreadGroup group, Runnable target, String name)        Thread t1 = new Thread(tg, my, "林青霞");     Thread t2 = new Thread(tg, my, "刘意");

        System.out.println(t1.getThreadGroup().getName());        System.out.println(t2.getThreadGroup().getName());

      //通过组名称设置后台线程,表示该组的线程都是后台线程        tg.setDaemon(true);   }

   private static void method1() {       MyRunnable my = new MyRunnable();        Thread t1 = new Thread(my, "林青霞");     Thread t2 = new Thread(my, "刘意");      // 我不知道他们属于那个线程组,我想知道,怎么办      // 线程类里面的方法:public final ThreadGroup getThreadGroup()      ThreadGroup tg1 = t1.getThreadGroup();       ThreadGroup tg2 = t2.getThreadGroup();       // 线程组里面的方法:public final String getName()      String name1 = tg1.getName();        String name2 = tg2.getName();        System.out.println(name1);        System.out.println(name2);        // 通过结果我们知道了:线程默认情况下属于main线程组      // 通过下面的测试,你应该能够看到,默任情况下,所有的线程都属于同一个组        System.out.println(Thread.currentThread().getThreadGroup().getName());    }}

生产者和消费者最终版本:
把Student的成员变量私有
把设置和获取的操作封装成功能,并添加同步
设置或获取的线程里只需要调用方法即可



public class Student { private String name;  private int age;  private boolean flag; // 默认情况是没有数据,如果是true,说明有数据

  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;        this.notify();    }

   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;       this.notify();    }}

public class GetThread implements Runnable {    private Student s;

  public GetThread(Student s) {     this.s = s;  }

   @Override    public void run() {       while (true) {            s.get();      } }}

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) {            if (x % 2 == 0) {               s.set("林青霞", 27);           } else {              s.set("刘意", 30);            }         x++;        } }}

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();   }}

线程池:

线程池的好处: 线程池里的每一个线程代码结束后,并不会死亡,
而是再次回到线程池中成为空闲状态,等待下一个对象使用。

如何实现线程池的代码?
A: 创建一个线程池对象,控制要创建几个线程对象。
public static ExecutorService newFixedThreadPool(int nThreds)
B: 这种线程池的线程可以执行:
可以执行Runnable对象或Callable对象代表的线程
做一个类实现Runnable接口
C: 调用如下方法即可
Future<?> submit(Runnable task)
T Future<T> submit(Callable task)



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 ExecutorsDemo {  public static void main(String[] args) {      // 创建一个线程池对象,控制要创建几个线程对象。      // public static ExecutorService newFixedThreadPool(int nThreads)     ExecutorService pool = Executors.newFixedThreadPool(2);

        // 可以执行Runnable对象或者Callable对象代表的线程        pool.submit(new MyRunnable());        pool.submit(new MyRunnable());

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

使用Callable实现



import java.util.concurrent.Callable;

//Callable:是带泛型的接口。//这里指定的泛型其实是call()方法的返回值类型。public class MyCallable implements Callable {

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

}

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

        //可以执行Runnable对象或者Callable对象代表的线程     pool.submit(new MyCallable());        pool.submit(new MyCallable());

      //结束      pool.shutdown();  }}

通过Callable可以返回泛型对象,实现求和



import java.util.concurrent.Callable;

/* * 线程求和案例 */public class MyCallable implements Callable<Integer> {

    private int number;

 public MyCallable(int number) {       this.number = number;    }

   @Override    public Integer call() throws Exception {      int sum = 0;     for (int x = 1; x <= number; x++) {            sum += x;       }     return sum;   }

}

public class CallableDemo {   public static void main(String[] args) throws InterruptedException, ExecutionException {      // 创建线程池对象        ExecutorService pool = Executors.newFixedThreadPool(2);

        // 可以执行Runnable对象或者Callable对象代表的线程        Future<Integer> f1 = pool.submit(new MyCallable(100));     Future<Integer> f2 = pool.submit(new MyCallable(200));

       // V get()        Integer i1 = f1.get();       Integer i2 = f2.get();

     System.out.println(i1);       System.out.println(i2);

     // 结束     pool.shutdown();  }}

使用匿名内部类实现线程
new 类名或者接口名(){
重写方法
}
本质:是该类或者接口的子类对象

public class ThreadDemo {    public static void main(String[] args) {      // 继承Thread类来实现多线程        new Thread() {            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 (int x = 0; x < 100; x++) {                 System.out.println(Thread.currentThread().getName() + ":"                          + x);                }         }     }) {      }.start();

      // 更有难度的,打印的是Thread类里重写的run()方法       new Thread(new Runnable() {           @Override            public void run() {               for (int x = 0; x < 100; x++) {                 System.out.println("hello" + ":" + x);              }         }     }) {          public void run() {               for (int x = 0; x < 100; x++) {                 System.out.println("world" + ":" + x);              }         }     }.start();    }}

定时器
可以让我们在指定的时间做某件事情,还可以重复的做某件事情。
依赖Timer和TimerTask这两个类:
Timer:定时
public Timer()
public void schedule(TimerTask task)
public void schedule(TimerTask task)
public void cancel()

TimerTask : 任务



public class TimerDemo {   public static void main(String[] args) {      // 创建定时器对象        Timer t = new Timer();       // 3秒后执行爆炸任务      // t.schedule(new MyTask(), 3000);        //结束任务        t.schedule(new MyTask(t), 3000);  }}

// 做一个任务class MyTask extends TimerTask {

  private Timer t;

    public MyTask(){}

   public MyTask(Timer t){       this.t = t;  }

   @Override    public void run() {       System.out.println("beng,爆炸了");     t.cancel();   }

}



public class TimerDemo2 {  public static void main(String[] args) {      // 创建定时器对象        Timer t = new Timer();       // 3秒后执行爆炸任务第一次,如果不成功,每隔2秒再继续炸      t.schedule(new MyTask2(), 3000, 2000);    }}

// 做一个任务class MyTask2 extends TimerTask {   @Override    public void run() {       System.out.println("beng,爆炸了"); }}



/* * 需求:在指定的时间删除我们的指定目录(你可以指定c盘,但是我不建议,我使用项目路径下的demo) */

class DeleteFolder extends TimerTask {

  @Override    public void run() {       File srcFolder = new File("demo");     deleteFolder(srcFolder);  }

   // 递归删除目录 public void deleteFolder(File srcFolder) {        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 class TimerTest {  public static void main(String[] args) throws ParseException {        Timer t = new Timer();

     String s = "2014-11-27 15:45:00";      SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");        Date d = sdf.parse(s);

     t.schedule(new DeleteFolder(), d);    }}

总结:

多线程(理解)
(1)JDK5以后的针对线程的锁定操作和释放操作
Lock锁
(2)死锁问题的描述和代码体现
(3)生产者和消费者多线程体现(线程间通信问题)
以学生作为资源来实现的

资源类:Student
设置数据类:SetThread(生产者)
获取数据类:GetThread(消费者)
测试类:StudentDemo

代码:
A:最基本的版本,只有一个数据。
B:改进版本,给出了不同的数据,并加入了同步机制
C:等待唤醒机制改进该程序,让数据能够实现依次的出现
wait()
notify()
notifyAll() (多生产多消费)
D:等待唤醒机制的代码优化。把数据及操作都写在了资源类中
(4)线程组
(5)线程池
(6)多线程实现的第三种方案
(7)多线程的面试题

传智播客风清扬视频-------线程简介2相关推荐

  1. 传智播客风清扬视频-------线程简介

    想了解线程,必须先了解进程,因为线程是依赖进程存在的. 什么是进程? 进程就是正在运行的程序:是系统进行资源分配和调用的独立单位.每一个进程都有它自己的内存空间和系统资源. 多进程有什么意义? 单进程 ...

  2. 传智播客风清扬视频-------网络编程简介

    计算机网络模型 OSI(Open System Interconnection开放系统互连)参考模型 TCP/IP参考模型 OSI 应用层--表示层--会话层--传输层--网络层--数据链路层--物理 ...

  3. 传智播客C语言视频第二季(第一季基础上增加诸多C语言案例讲解,有效下载期为10.5-10.10关闭

    分享一下我老师大神的人工智能教程!零基础,通俗易懂!http://blog.csdn.net/jiangjunshow 也欢迎大家转载本篇文章.分享知识,造福人民,实现我们中华民族伟大复兴! 卷 ba ...

  4. 传智播客C语言视频第二季(第一季基础上增加诸多C语言案例讲解,有效下载期为10.5-10.10关闭)

    卷 backup 的文件夹 PATH 列表 卷序列号为 00000025 D4A8:14B0 J:. │  1.txt │  c语言经典案例效果图示.doc │  ├─1传智播客_尹成_C语言从菜鸟到 ...

  5. 传智播客C语言视频第二季(第一季基础上增加诸多C语言案例讲解,有效下载期为10.5-10.10关闭)...

    卷 backup 的文件夹 PATH 列表 卷序列号为 00000025 D4A8:14B0 J:. │  1.txt │  c语言经典案例效果图示.doc │  ├─1传智播客_尹成_C语言从菜鸟到 ...

  6. 传智播客C语言视频第二季 第一季基础上增加诸多C语言案例讲解,有效下载期为10 5-10 10关闭

    分享一下我老师大神的人工智能教程.零基础!通俗易懂!风趣幽默!还带黄段子!希望你也加入到我们人工智能的队伍中来!https://blog.csdn.net/jiangjunshow 卷 backup ...

  7. 传智播客-php基础视频,传智播客PHP核心基础视频教程推荐(资源)

    PHP,是英文超级文本预处理语言Hypertext Preprocessor的缩写.PHP 是一种 HTML 内嵌式的语言,是一种在服务器端执行的嵌入HTML文档的脚本语言,语言的风格有类似于C语言, ...

  8. java工作流 传智播客_Activiti工作流视频教学(企业开发实战讲解)_传智播客

    Activiti工作流视频教学(企业开发实战讲解)_传智播客课程简介: Activiti工作流视频教学(企业开发实战讲解)_传智播客本教学共分4天进行讲解,本站提供第1天内容在线观看,全集教学请在本站 ...

  9. 传智播客软件测试学习视频汇总:

    课程名称 分类 URL 提取码 软件测试入门到精通 视频 http://yun.itheima.com/course/490.html?aoe cnj1 资料 https://pan.baidu.co ...

  10. Java多线程(6)——Thread类中的一些方法(传智播客毕老师视频讲解)

    1.守护线程 代码如下: import java.util.concurrent.locks.*; public class StopTest implements Runnable {private ...

最新文章

  1. python不能创建新变量_Python之变量的创建过程!
  2. 拥有懂需求的云计算供应商,是一种怎样的体验
  3. Form表单中的button导致页面刷新而无法进入Ajax请求回调函数
  4. k8s部署tomcat及web应用_部署 Spring Boot 应用到 K8S 教程
  5. 熬夜都要看完的 Spring 干货!
  6. File “/usr/bin/yum“, line 30 及 File “/usr/libexec/urlgrabber-ext-down“, line 28
  7. 一分钟系列:详解阿里云68款产品【热点问题+用户实践】
  8. Linux下MongoDB安装和配置详解
  9. [DFA|有限状态机] leetcode 8 字符串转换整数(atoi)
  10. 使用js来实现模拟无刷新文件上传。
  11. Routerboard/DR4019S-Qualcomm-IPQ4019-2T2R-Dual-Band-2-4GHz-5GHz-support-OpenWRT-802.11ac-Wave-2.
  12. LabVIEW心率监测装置
  13. cad动态块制作翻转_CAD中怎么定义旋转动态块?
  14. directx 11 64位
  15. 37岁Java程序员求职6K+职位被怼,你怎么看?
  16. 犹他州计算机科学,犹他州大学计算机科学computer science专业排名第201~250名(2020THE泰晤士高等教育世界大学排名)...
  17. 均值和方差的计算(已知两样本标准差,求总体标准差)
  18. 本地消息表(异步确保)
  19. 微信小程序—自定义组件
  20. 网络地址转换协议NAT功能详解+NAT配套练习题

热门文章

  1. 永洪科技怎么样_【永洪科技怎么样?】-看准网
  2. No query specified(Mysql数据库报错)
  3. C盘空间不足,UE4的deriveddatacache目录位置修改
  4. cbac式_CBAC
  5. js校验 身份证号18位
  6. android blowfish加密算法,blowfish 现在哪种加密算法安全?A
  7. 32位系统和64位区别
  8. 360随身WiFi驱动下载
  9. 第四章—密钥管理与分配
  10. Saas平台接入商户代小程序开发解决方案