------<a href="http://www.itheima.com" target="blank">Java培训、Android培训、iOS培训、.Net培训</a>、期待与您交流! -------
1.线程是程序执行的一条路径, 一个进程中可以包含多条线程,多线程并发执行可以提高程序的效率, 可以同时完成多项工作。
2.开启新线程的两种方式
1).继承Thread
- public static void main(String[] args) {
- MyThread mt = new MyThread(); //4,创建自定义类对象
- mt.start(); <span style="font-family:宋体;">//5,开启线程
- </span> for(int i = 0; i < 10000; i++) {
- System.out.println("传智播客");
- }
- }
- }
- class MyThread extends Thread { //1,自定义类继承Thread
- public void run() { //2,重写run方法
- for(int i = 0; i < 10000; i++) { //3,将要执行的代码写在run方法中
- System.out.println("黑马程序员");
- }
- }
- }
2).实现Runnable
- public static void main(String[] args) {
- MyRunnable mr = new MyRunnable();//4,创建自定义类对象
- Thread t = new Thread(mr); //5,将自定义类对象当作参数传递给Thread的构造函数
- t.start(); //6,开启线程
- for(int i = 0; i < 10000; i++) {
- System.out.println("传智播客");
- }
- }
- }
- class MyRunnable implements Runnable { //1,自定义类实现Runnable接口
- @Override
- public void run() { //2,重写run方法
- for(int i = 0; i < 10000; i++) {//3,将要执行的代码写在run方法中
- System.out.println("黑马程序员");
- }
- }
- }
3.Thread类常用方法
获取名字: getName()方法
设置名字:通过构造函数可以传入String类型的名字或通过setName(String)方法可以设置线程对象的名字
获取当前线程对象:Thread.currentThread()
休眠:Thread.sleep(毫秒,纳秒)
守护:setDaemon()
加入:
join(), 当前线程暂停, 等待指定的线程执行结束后, 当前线程再继续
join(int), 可以等待指定的毫秒之后继续
让出cpu:yield
设置线程的优先级:setPriority()
- public static void main(String[] args) {
- final Thread t1 = new Thread() {
- public void run() {
- for(int i = 0; i < 50; i++) {
- try {
- Thread.sleep(10);//休眠
- } catch (InterruptedException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- System.out.println(getName() + "...传智播客");
- }
- }
- };
- Thread t2 = new Thread(new Runnable(){
- public void run() {
- for(int i = 0; i < 50; i++) {
- if(i == 2) {
- try {
- //t1.join();//让出cpu,只有t1完全执行完,再执行t2
- t1.join(1);//让出cpu,但是只让出1毫秒,就是指定参数的时间
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- System.out.println(Thread.currentThread().getName() + "...黑马程序员");
- } //获取当前线程对象
- }
- });
- t1.setDaemon(true); //设置t1为守护线程
- t1.start();
- t2.start();
- }
4.线程之间的同步
线程之间的同步有同步代码块和同步方法
同步代码块是使用synchronized关键字加上一个锁对象来定义一段代码,多个同步代码块使用相同的锁对象
同步方法是使用synchronized关键字修饰一个方法, 该方法中所有的代码都是同步的,非静态同步方法默认使用当 前对象this作为锁对象,静态方同步函数的锁是该类的字节码对象。字节码对象可以认为是万能锁。
要注意的是多线程同步的时候, 如果同步代码嵌套, 使用相同锁, 就有可能出现死锁,所以尽可能不要嵌套使用。
例如:模拟售票,总数为一千张,有四个窗口卖票:
- public class Ticket {
- public static void main(String[] args) {
- new TicketsSeller().start(); //开启线程
- new TicketsSeller().start();
- new TicketsSeller().start();
- new TicketsSeller().start();
- }
- }
- class TicketsSeller extends Thread {
- private static int tickets = 1000;
- public void run() {
- while(true) {
- synchronized(TicketsSeller.class) {
- if(tickets == 0)
- break;
- try {
- Thread.sleep(10);//增强效果
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- System.out.println(getName() + "...这是第" + tickets-- + "号票");//卖票,并将结果--
- }
- }
- }
- }
5.单例设计模式
单例设计模式:保证类在内存中只有一个对象。
(1)将构造方法私有。
(2)在本类中定义一个本类的对象。Singleton s;
(3)提供公共的访问方式。public static Singleton getInstance(){return s}
单例写法两种:饿汉式,懒汉式;
饿汉式:
- class Singleton{
- private Singleton(){}
- private static Singleton s=new Singleton();
- public static Singleton getInstance(){
- return s;
- }
- }
懒汉式:
- class Singleton{
- private Sinleton(){}
- private static Singleton s;
- public static Sinleton getInstance(){
- if(s==null){
- s=new Sinleton();}
- return s;
- }
- }
第三种格式:
- class Singleton {
- private Singleton() {}
- public static final Singleton s = new Singleton();//final是最终的意思,被final修饰的变量不可以被更改
- }
6.线程之间的通信
线程等待, wait()
唤醒等待 notify();
这两个方法必须在同步代码中执行, 并且使用同步锁对象来调用
多个线程通信的问题
notify()方法是随机唤醒一个线程
notifyAll()方法是唤醒所有线程
JDK5之前无法唤醒指定的一个线程
如果多个线程之间通信, 需要使用notifyAll()通知所有线程, 用while来反复判断条件
- public class Demo6_Notify {
- public static void main(String[] args) {
- final Printer p = new Printer();
- new Thread() {
- public void run() {
- while(true) {
- try {
- p.print1();
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- }
- }.start();
- new Thread() {
- public void run() {
- while(true) {
- try {
- p.print2();
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- }
- }.start();
- }
- }
- class Printer {
- private int flag = 1;
- public void print1() throws InterruptedException {
- synchronized(this){
- if(flag != 1)
- this.wait(); //线程等待
- System.out.print("传");
- System.out.print("智");
- System.out.print("播");
- System.out.print("客");
- System.out.print("\r\n");
- flag = 2; //flag改为2
- this.notify(); //唤醒等待的线程
- }
- }
- public void print2() throws InterruptedException {
- synchronized(this){
- if(flag != 2)
- this.wait(); //线程等待
- System.out.print("黑");
- System.out.print("马");
- System.out.print("程");
- System.out.print("序");
- System.out.print("员");
- System.out.print("\r\n");
- flag = 1; //flag改为1
- this.notify(); //唤醒等待的线程
- }
- }
7.JDK5之后的线程控制
同步
使用ReentrantLock类的lock()和unlock()方法进行同步
通信
使用ReentrantLock类的newCondition()方法可以获取Condition对象
需要等待的时候使用Condition的await()方法, 唤醒的时候用signal()方法
不同的线程使用不同的Condition, 这样就能区分唤醒的时候找哪个线程了
- package cn.itcast.thread;
- import java.util.concurrent.locks.Condition;
- import java.util.concurrent.locks.ReentrantLock;
- public class Demo8_ReentrantLock{
- public static void main(String[] args) {
- final Printer3 p = new Printer3();
- new Thread() {
- public void run() {
- while(true) {
- try {
- p.print1();
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- }
- }.start();
- new Thread() {
- public void run() {
- while(true) {
- try {
- p.print2();
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- }
- }.start();
- new Thread() {
- public void run() {
- while(true) {
- try {
- p.print3();
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- }
- }.start();
- }
- }
- class Printer3 {
- private ReentrantLock r = new ReentrantLock(); //创建锁对象
- private Condition c1 = r.newCondition(); //添加监视器
- private Condition c2 = r.newCondition();
- private Condition c3 = r.newCondition();
- private int flag = 1;
- public void print1() throws InterruptedException {
- r.lock(); //获取锁
- if(flag != 1)
- c1.await(); //线程等待
- System.out.print("传");
- System.out.print("智");
- System.out.print("播");
- System.out.print("客");
- System.out.print("\r\n");
- flag = 2; //flag改为2
- c2.signal(); //唤醒线程2
- r.unlock(); //释放锁
- }
- public void print2() throws InterruptedException {
- r.lock(); //获取锁
- if(flag != 2) {
- c2.await(); //线程等待,2线程等待
- }
- System.out.print("黑");
- System.out.print("马");
- System.out.print("程");
- System.out.print("序");
- System.out.print("员");
- System.out.print("\r\n");
- flag = 3; //flag改为3
- c3.signal(); //唤醒线程3
- r.unlock(); //释放锁
- }
- public void print3() throws InterruptedException {
- r.lock(); //获取锁
- if(flag != 3) {
- c3.await(); //线程等待,3线程等待
- }
- System.out.print("i");
- System.out.print("t");
- System.out.print("c");
- System.out.print("a");
- System.out.print("s");
- System.out.print("t");
- System.out.print("\r\n");
- flag = 1; //flag改为1
- c1.signal(); //唤醒线程1
- r.unlock(); //释放锁
- }
- }