黑马程序员-自学多线程

------<a href="http://www.itheima.com" target="blank">Java培训、Android培训、iOS培训、.Net培训</a>、期待与您交流! -------


概念

进程:正在执行中的程序。

每个进程执行都有一个执行顺序,该顺序是一个执行路径,或者叫一个控制单元。

 

线程:进程中一个独立的控制单元。

线程控制着进程的执行。

一个进程中至少有一个线程。

 

l 创建线程的第一种方法。

步骤:

1. 创建类继承Thread

2. 重写Threadrun方法。

3. 调用线程的start方法。该方法两个作用:1.启动线程,2.调用该线程run方法

*** start方法与run方法:

start方法启动了线程,调用该线程的run方法。

run方法只是对象调用方法。虽然线程创建了,但是没有被运行。

 

l 线程的创建过程

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

l 获取线程对象以及名称

getName() Thread-[0,1... ...]

currentThread() static

设置线程的名称:setName() 或者 构造函数调用super(String Name)

 

l 售票窗口卖票的例子(创建线程的第二种方法)

四个线程同时卖票

一、继承Thread方法创建线程。

public static void main(String[] args) {

// 注意:如果没有使用static关键词,则每一次都会创建一个ticket=100

TicketTest t1 = new TicketTest("窗口一");

TicketTest t2 = new TicketTest("窗口二");

TicketTest t3 = new TicketTest("窗口三");

 

t1.start();

t2.start();

t3.start();

}

}

 

class TicketTest extends Thread{

// 该构造方法,通过调用父类方法,直接给将要创建的新的线程赋予新的名字。

public TicketTest(String name) {

super(name);

}

// 使用static关键字,ticket变为静态。

//private static int ticket = 100;

private int ticket = 100;

// 必须要重写Thread类的run方法。

public void run() {

while(true) {

if(ticket > 0)

System.out.println(Thread.currentThread().getName()+ "还剩:"+(ticket--)+" 张票");

}

}

}

*如果一个线程通过start()方法开始运行了,再多余的start()已经没有意义了,则会报错:

t1.start();

t1.start();

t1.start();

java.lang.IllegalThreadStateException

 

二、实现Runnable接口创建线程。

public static void main(String[] args) {

TicketTest tt = new TicketTest();

Thread t1 = new Thread(tt);

Thread t2 = new Thread(tt);

Thread t3 = new Thread(tt);

t1.start();

t2.start();

t3.start();

}

}

 

class TicketTest implements Runnable /*extends Thread*/{

private int ticket = 100;

// 必须要重写Thread类的run方法。

public void run() {

while(true) {

if(ticket > 0)

System.out.println(Thread.currentThread().getName()+ "还剩:"+(ticket--)+" 张票");

}

}

}

 

 

创建线程的第二种方法

1. 实现Runnable接口

2. 实现Runnable接口的run方法

3. 创建一个ThreadThread实例

4. 将实现了Runnable接口的类作为对象传递给创建的Thread实例

5. 调用Thread实例的start方法。

*注:大多数情况下,如果只重写run方法,二部重写Thread的其他类,那么应该使用Runnable接口。

l 区别:implement Runnable & extends Thread

 

 

 

 

 

 

 

 

 

 

 

 

 

1. 避免了java只能单继承。

2. 实现Runnable接口的类的run方法写在Runnable子类中。

3. 继承Thread类的类的run方法写在Thread子类中。

 

l 多线程的安全问题

while(true) {

if(ticket > 0){

try {

Thread.sleep(10);

catch (InterruptedException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

System.out.println(Thread.currentThread().getName()+ "还剩:"+(ticket--)+" 张票");

}

}

说明:当票数被剪掉以前,所有的线程都经过ticket > 0判断语句后被分配到了执行权,停止在票数剪掉之前的地方,那么都将会执行ticket--的代码,从而使ticket的数目小于0

 

解决方法1

l 同步

实现同步的两个先决条件:

1. 大于等于两个线程。

2. 所有线程使用同一个锁。(对于同时包含同步代码和同步函数的情况,同步代码使用this锁;静态同步方法使用该类的字节码对象)

 

(一)同步代码块:

格式:

synchronized(对象) {

...代码

}

对变量进行判断和操作的代码进行同步操作。

private int ticket = 100;

// 这个对象相当于锁,这个锁要放在run方法以外,让所有的线程来共享这个锁。

Object obj = new Object();

// 必须要重写Thread类的run方法。

public void run() {

while(true) {

// 同步代码块

synchronized(obj) {

if(ticket > 0){

try {

Thread.sleep(10);

catch (InterruptedException e) {

// TODO Auto-generated catch block

e.printStackTrace();

} System.out.println(Thread.currentThread().getName()+ "还剩:"+(ticket--)+" 张票");

}

}

}

}

 

(二)同步函数

格式:

public synchronized xxx() {

...

}

同步函数的锁旗标是this

 

同步静态方法

使用的锁是该类所属的字节码文件对象,即 类名.class

 

l 死锁

当两个线程同时获取了一个不同锁Lock1Lock2,但下面的各线程又将要获取对方手中的锁,这种情况下,程序会停滞住。

有时会出现和谐的现象。

 

public class TestDeadLock{

public static void main(String[] args) {

DeadLock dl = new DeadLock();

Thread t1 = new Thread(dl);

Thread t2 = new Thread(dl);

dl.flag = true;

t1.start();

try {

Thread.sleep(100);

catch (InterruptedException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

dl.flag = false;

t2.start();

}

}

 

class Lock {

static Object lock1 = new Object();

static Object lock2 = new Object();

}

 

class DeadLock implements Runnable {

public boolean flag = true;

public void run() {

if(flag == true) {

while(true){

synchronized(Lock.lock1) {

System.out.println("if中的Lock1");

synchronized(Lock.lock2) {

System.out.println("if中的Lock2");

}

}

}

else {

while(true){

synchronized(Lock.lock2) {

System.out.println("else中的Lock2");

synchronized(Lock.lock1) {

System.out.println("else中的Lock1");

}

}

}

}

}

}

输出结果:

 

 

 

l 线程间的通信

 

 

 

 

 

 

 

 

 

 

 

 

l wait() - notify() 机制。

当线程在运行时,会产生一个线程池,所有wait的线程都会放到线程池中,notify()方法会唤醒线程池中的第一个线程。

 

package com.lxh.thread;

/**

 * 解决线程同步的两个先决条件

 * 1. 两个或两个以上的线程

 * 2. 同一个锁

 * 在这个例子中,被共享操作的资源分布在两个线程的run方法中,

 * 所以对着两部分代码的共享资源,都要进行同步。

 * */

 

public class InputOutputDemo {

 

public static void main(String[] args) {

Source s = new Source();

Input input = new Input(s);

Output output = new Output(s);

 

Thread t1 = new Thread(input);

Thread t2 = new Thread(output);

 

t1.start();

t2.start();

}

}

 

class Input implements Runnable {

private Source s ;

public Input(Source s) {

this.s = s;

}

private int i;

public void run() {

while(true) { //对同一个对象的变量进行无限次的赋值

synchronized(s){

if(s.flag)

try {

s.wait();

catch (InterruptedException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

 

if(i == 1) {

s.place = "DaTong";

s.color = "red";

i++;

else {

s.place = "攀枝花";

s.color = "黑色";

i++;

}

s.flag = true;

s.notify();

}

i = i%2;

}

}

}

 

class Output implements Runnable {

private Source s ;

public Output(Source s) {

this.s = s;

}

public void run() {

while(true){ 对同一个对象的变量进行无限次的取值 

synchronized(s){

if(!s.flag)

try {

s.wait();

catch (InterruptedException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

System.out.println("产地:" + s.place + " 颜色:" + s.color );

s.flag = false;

s.notify();

}

}

}

}

 

class Source {

public String place;

public String color;

public boolean flag;

}

 

 

***注意:

只有同一个锁上的同一个wait的线程才能被同一个锁上的notify线程锁唤醒。不可以对不同锁中的线程进行唤醒。

也就是说:等待和唤醒是同一把锁。

 

而锁可以是任意对象,所以notify


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值