---------- android培训、java培训、java学习型技术博客、期待与您交流! ----------
1、先弄懂两个词:进程、线程。
进程:是一个正在执行中的程序。每一个进程执行都有一个顺序,该顺序是一个路径,或者叫一个控制单元。
线程:是进程中的一个独立的控制单元。线程在控制着进程的执行。一个进程中至少有一个线程。
java vm 启动的时候,会有一个进程,叫java.exe。该进程中至少有一个线程,在负责java程序的执行,而且这个线程运行的代码存在于main方法中。该线程叫做 “主线程”。
多线程的一个特性:随机性。谁抢到谁执行。至于执行多长时间,cpu说了算。
2、创建线程有两种方法:
1.1 创建线程的第一种方法:继承Thread类
(1)子类覆盖父类中的run方法,将线程运行的代码存放在run中。
(2)建立子类对象的同时线程也被创建。
(3)start方法的两个作用:通过调用 start方法开启线程并同时调用run方法。
1.2 实现Runnable接口
(1)定义类实现Runnable接口。
(2)覆盖Runnable接口中的run方法。
(3)通过Thread类建立线程对象。
(4)将Runnable接口的子类对象作为实际参数传递给Thread类的构造函数。
为什么要将Runnable接口的子类对象传递给Thread的构造函数?
因为自定义的run方法所属的对象是Runnable接口的子类对象.所以要让线程去指定指定对象的run方法。就必须明确该run方法所属对象。
(5)调用Thread类的start方法开启线程并调用Runnable接口子类的run方法。
实现方式和继承方式有什么区别呢?
实现方式好处:避免了单继承的局限性。
建议使用第二种方式。
两种方式的区别:
继承Thread:线程代码存放在Thread子类run方法中
实现Runnable:线程代码存放在接口的子类的run方法中
3、多线程安全问题原因:
当多条语句在操作同一个线程共享数据时,一个线程对多条语句只执行了一部分,还没有执行完,另一个线程参与进来,导致了共享数据的错误
解决办法:对多条操作共享数据的语句,只能让一个线程都执行完。在执行的过程中其他线程不可以参与执行。同步代码。
class Tickets implements Runnable {
int tickets = 100;
public void run() {
//同步代码块,线程0进入,其他线程在外面等待,
//等线程0执行完了,其他线程中的一条线程获得监视器,进入,以此循环
synchronized (this) {
while (tickets > 0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()
+ "--sale : " + tickets--);
}
}
System.out.println("over");
}
}
public class Thread_3_MaiPiao {
public static void main(String[] args) {
Tickets tickets = new Tickets();
Thread t1 = new Thread(tickets);
Thread t2 = new Thread(tickets);
Thread t3 = new Thread(tickets);
Thread t4 = new Thread(tickets);
t1.start();
t2.start();
t3.start();
t4.start();
}
}
任意对象都有一个标志位,有0和1两种状态。
当一个线程获得监视器时,状态改为0,执行完同步代码后,改为1。其他线程来争夺监视器。
同步函数的锁是this;静态同步函数的锁是Class对象(类字节码)
4、线程间的通信
wait()方法和sleep(time)方法的区别?
wait():释放资源,释放锁
sleep(time):释放资源,不释放锁
package day11_12;
class Producer implements Runnable {
private Clerk clerk;
public Producer(Clerk clerk) {
this.clerk = clerk;
}
@Override
public void run() {
System.out.println("生产者开始生产整数......");
// 生产1到10的整数
for (int product = 1; product <= 10; product++) {
try {
// 随即暂停时间
Thread.sleep((int) Math.random() * 3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 将产品交给店员
clerk.setProduct(product);
}
}
}
class Consumer implements Runnable {
private Clerk clerk;
public Consumer(Clerk clerk) {
this.clerk = clerk;
}
@Override
public void run() {
System.out.println("消费者开始消耗整数......");
// 生产1到10的整数
for (int product = 1; product <= 10; product++) {
try {
// 随即暂停时间
Thread.sleep((int) Math.random() * 3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 将产品交给店员
clerk.getProduct();
}
}
}
// 资源
class Clerk {
// -1表示没有商品
private int product = -1;
public synchronized void setProduct(int product) {
if (this.product != -1) {
try {
// 目前店员没有空间收产品,请稍候!
wait();// 自己暂停了
} catch (InterruptedException e) {
e.printStackTrace();
}
}
this.product = product;
System.out.println("生产者设定" + this.product);
// 通知等待区中的一个消费者可以继续工作了
notify();// 唤醒消费者
}
public synchronized int getProduct() {
if (this.product == -1) {
try {
// 目前缺货,请稍候!
wait();// 自己暂停了
} catch (InterruptedException e) {
e.printStackTrace();
}
}
int p = this.product;
System.out.println("消费者取走" + this.product);
this.product = -1;
notify();// 唤醒生产者生产
return p;
}
}
public class Communication {
public static void main(String[] args) {
Clerk clerk = new Clerk();
Thread t1 = new Thread(new Producer(clerk));
Thread t2 = new Thread(new Consumer(clerk));
t1.start();
t2.start();
}
}
5、JDK1.5线程升级版
import java.util.concurrent.locks.*;
/**
* 生产者消费者,JDK1.5版多线程实现
*/
public class Thread_8_ProducerConsumerDemo {
public static void main(String[] args) {
Resource3 res = new Resource3();
Producer3 p = new Producer3(res);
Consumer3 c = new Consumer3(res);
Thread t1 = new Thread(p);
Thread t2 = new Thread(p);
Thread t3 = new Thread(c);
Thread t4 = new Thread(c);
t1.start();
t2.start();
t3.start();
t4.start();
}
}
class Resource3{
private String name;
private int count = 1;
private boolean flag = false;
//锁
private Lock lock = new ReentrantLock();
//获取条件,包含了等待唤醒方法
Condition condition = lock.newCondition();
public void set(String name) throws InterruptedException {
//同步代码块使用了lock的两个方法代替
lock.lock();
try{
while(this.flag)
condition.await();
this.name = name+"--"+(count++);
System.out.println(Thread.currentThread().getName()+"...生产者..."+this.name);
this.flag=true;
condition.signalAll();
}finally{
//释放锁一定要放在finally
lock.unlock();
}
}
public void out() throws InterruptedException{
lock.lock();
try{
while(!this.flag)
condition.await();
System.out.println(Thread.currentThread().getName()+"......消费者......"+this.name);
this.flag=false;
condition.signalAll();
}finally{
lock.unlock();
}
}
}
class Producer3 implements Runnable{
private Resource3 res = null;
Producer3(Resource3 res){
this.res=res;
}
public void run() {
while(true){
try {
res.set("+商品+");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class Consumer3 implements Runnable{
private Resource3 res = null;
Consumer3(Resource3 res){
this.res=res;
}
public void run() {
while(true){
try {
this.res.out();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
6、停止线程
如何停止线程?
只有一种,run方法结束。开启线程,运行代码通常是循环结构。
只要控制住循环,就可以让run方法结束,也就是线程结束。
interrupt方法:强制将线程从冻结状态变成运行状态,将会收到一个异常
class StopThread implements Runnable{
boolean flag = true;
public synchronized void run() {
while(flag){
try{
wait();
}catch(InterruptedException e){
System.out.println(Thread.currentThread().getName()+"...exception...");
//发生异常,改变线程标记,结束线程
flag = false;
}
System.out.println(Thread.currentThread().getName()+"...run...");
}
}
}
public class Thread_9_StopThread {
public static void main(String[] args) {
StopThread stopThread = new StopThread();
Thread t1 = new Thread(stopThread);
Thread t2 = new Thread(stopThread);
t1.start();
t2.start();
int count = 0;
while(true){
System.out.println(Thread.currentThread().getId()+"..main.."+count);
if(count++ ==60){
t1.interrupt();
t2.interrupt();
break;
}
}
}
}
7、守护线程
setDaemon
public final void setDaemon(boolean on)
将该线程标记为守护线程或用户线程。当正在运行的线程都是守护线程时,Java 虚拟机退出。
该方法必须在启动线程前调用。
该方法首先调用该线程的 checkAccess 方法,且不带任何参数。这可能抛出 SecurityException(在当前线程中)。
参数:
on- 如果为 true,则将该线程标记为守护线程。
8、Join方法
/**
* 当A线程执行到B线程的.join()方法时,A就会等待。等B线程
* 都执行完了,A才会执行。
* join可以用来临时加入线程执行。
*/
class Demo1 implements Runnable{
public void run() {
for(int i=0;i<70;i++){
System.out.println(Thread.currentThread().getName()+"---"+i);
}
}
}
public class Thread_10_join {
public static void main(String[] args) throws InterruptedException {
Demo1 demo = new Demo1();
Thread t1 = new Thread(demo);
Thread t2 = new Thread(demo);
t1.start();
t1.join();//申请CPU执行权,先执行完t1
t2.start();
for(int i=0;i<80;i++){
System.out.println(Thread.currentThread().getName()+"==="+i);
}
System.out.println("over");
}
}
9、yield()
使当前正在运行的线程对象暂停一下,让其他线程执行。
个人总结
在学习线程时,要弄懂为什么要建议使用Runnable、卖票程序、线程安全、线程通信,JDK1.5线程升级版
----------android培训、java培训、java学习型技术博客、期待与您交流! ----------
详细请查看:http://edu.csdn.net/heima