----------- android培训、java培训、期待与您交流! ------------
线程概念
进程:正在进行中的程序
线程:进程中的多条执行路径
线程控制着进程的执行进度。
一个进程中至少有一个线程(主线程)。
多线程的一个特性:随机性。
每个线程都获取CPU的执行权,到底谁执行、执行多久由CPU来决定。多个线程之间交替执行。
启动线程的唯一方法是:线程类对象.start();
用于开启线程并执行线程中的run();
线程类的run()方法,用于封装要运行的代码。
线程创建
下面是两种创建线程的方式:
public class MultiThread {
public static void main(String[] args) {
//创建并启动线程一
//new Demo().start();
//创建并启动线程二
new Thread(new Demo2()).start();
//主线程执行程序
for(int i=0; i<1000; i++) {
System.out.println(Thread.currentThread().getName() + ": The world " + i);
}
}
}
//定义线程的第一种方法:继承自Thread类
class Demo extends Thread {
public void run() { //封装要运行的代码
for(int i=0; i<1000; i++) {
System.out.println(Thread.currentThread().getName() + ": Hello java " + i);
}
}
}
//定义线程的第二种方法:实现Runnable接口
class Demo2 implements Runnable {
public void run() { //封装要运行的代码
for(int i=0; i<1000; i++) {
System.out.println(Thread.currentThread().getName() + ": Hello java " + i);
}
}
}
实现方式和继承方式的区别?
1、继承Thread,线程运行代码存放在Thread子类run方法中
实现Runnable,线程运行代码存放在Runnable接口子类run方法中
2、实现方式避免了java单继承的局限性。
在定义线程类时,建议使用实现方式
线程状态
线程的状态示意图:
多窗口卖票示例:
(若继承Thread,则Ticket的ticket属性应为static)
public class SaleTicket {
public static void main(String[] args) {
Ticket t = new Ticket();
new Thread(t).start();
new Thread(t).start();
new Thread(t).start();
new Thread(t).start();
}
}
class Ticket implements Runnable {
private int ticket = 100;
public void run() {
while(ticket > 0) {
System.out.println(Thread.currentThread().getName() + "sale : " + ticket--);
}
}
}
多线程安全问题
问题出现的状况:
当多个线程在操作同一个共享数据时,一个线程的多条语句中只执行了一部分,CPU执行权即分配到了其它的线程中执行。导致共享数据错误。
解决办法:
单个线程在操作共享数据时,其它线程不能参与进来。
java用的1、同步代码块来实现安全:
synchronized(对象) {
需要被同步的语句;
}
对象如同锁,没有锁的线程就算取得了CPU的执行权也不能执行。
线程加锁带来的弊端:要有锁对象,所以耗资源;要判断锁,所以效率稍减。
2、同步函数
public synchronized void method(Type args) {
需要被同步的语句;
}
同步非静态函数用的锁是this。
如果同步静态函数:所用的锁不是this,因为静态方法中不能出现this。
用的是 类名.class是Class类型对象。
如果一个程序中有安全问题,使用同步时应注意:
1、明确多线程运行代码(一般为run方法里调用的语句,以及其附带语句(调用了其它的方法))有哪些 2、明确共享数据为何 3、明确运行代码中有哪些语句操作共享数据
使用同步还会出安全问题:
此时定为同步的两个前提条件中的一个不满足所造成:1、必须有两个或两个以上的线程 2、必须是多个线程使用同一把锁。
单例设计模式
饿汉式和懒汉式有什么区别?
1、懒汉式特点:实例的延时加载。
2、懒汉式在多线程访问时存在安全问题(为什么?答:获取实例的方法中有多条语句操作共享资源),如何解决?
答:用同步代码块和同步函数都能解决此问题,但效率稍低;可以用双重判断来减少判断锁的次数。
3、懒汉式加同步时使用的锁是哪个?
答:该类所属的字节码文件对象。
class ESingle { //饿汉式
private static final ESingle es = new ESingle();
private ESingle() {}
public static ESingle getInstance() {
return es;
}
}
class LSingle { //懒汉式
private static LSingle ls;
private LSingle() {}
public static LSingle getInstance() {
if(ls == null) { //双重判断减少对锁的判断次数
synchronized(LSingle.class) {
if(ls == null) {
ls = new LSingle();
}
}
}
return ls;
}
}
死锁
死锁的产生:同步中嵌套同步,而使用的锁不一样。
死锁代码示例:
public class DeadLock {
public static void main(String[] args) {
Test a = new Test(true);
Test b = new Test(false);
Thread t1 = new Thread(a);
Thread t2 = new Thread(b);
t1.start();
t2.start();
}
}
class Test implements Runnable {
private boolean flag;
Test(boolean flag) {
this.flag = flag;
}
public void run() {
if(flag) {
synchronized(MyLock.locka) {
System.out.println("if...locka");
synchronized(MyLock.lockb) {
System.out.println("if...lockb");
}
}
}
else {
synchronized(MyLock.lockb) {
System.out.println("else...lockb");
synchronized(MyLock.locka) {
System.out.println("else...locka");
}
}
}
}
}
class MyLock {
static Object locka = new Object();
static Object lockb = new Object();
}
多线程通信
多线程通信:即多个线程操作同一个资源,但操作的动作不同。
等待唤醒机制:
public class ThreadCommunication{
public static void main(String[] args) {
Res r = new Res();
new Thread(new Input(r)).start();
new Thread(new Output(r)).start();
}
}
//共享资源
class Res {
private String name;
private String sex;
private boolean flag = false;
//设置方法
public synchronized void set(String name, String sex){
if(flag)//flag=true表示设置过的还未打印,有数据则等待
try{this.wait();}catch(InterruptedException e){e.printStackTrace();}
this.name = name;
this.sex = sex;
flag = true;
this.notify();
}
//打印方法
public synchronized void out(){
if(!flag)
try{this.wait();}catch(InterruptedException e){e.printStackTrace();}
System.out.println(name + ".........." + sex);
flag = false;
this.notify();
}
}
//输入线程
class Input implements Runnable{
private Res r;
Input(Res r) {
this.r = r;
}
public void run() {
int x = 0;
while(true) {
if(x==0) {
r.set("lisi", "man");
}
else {
r.set("丽丽", "女女女女");
}
x = (x+1)%2;//控制x在0和1之间不断交替,从而让设置的内容不同。
}
}
}
//输出线程
class Output implements Runnable{
private Res r;
Output(Res r) {
this.r = r;
}
public void run() {
while(true) {
r.out();
}
}
}
在线程池里等待的线程,往往先唤醒的是第一个。
wait()、notify()、notifyAll()。当前线程必须拥有此对象监视器(即锁),锁只有在同步中才有。
上述方法为何被定义在Object类中?
答:上述方法由锁调用,锁可以是任意对象,所以定义在任意对象的父类Object中。
wait()和sleep()有何区别?
答:wait()释放资源、释放锁;sleep()释放资源、不释放锁。
等待和唤醒的必须是同一个锁。而锁可以是任意对象,所以定义在Object中。
Lock接口和Condition接口
JDK1.5中提供了替代同步中隐式锁的synchronized为显式锁方式Lock接口和Condition接口
* 将Object中的wait()、notify()、notifyAll()替换成了Condition对象的方法。
* Condition对象可以由Lock获取
* 实现了本方只会唤醒对方的操作。
*
* 生产者消费者问题有什么替代方案?
* 答:1.5版本时,提供了显式的锁机制,以及显式的锁对象上的等待唤醒操作机制
* 一个Lock锁,对应了多个Condition
public class ProducerConsumerJDK5 {
public static void main(String[] args) {
Resource res = new Resource();
Producer p1 = new Producer(res);
Consumer c1 = new Consumer(res);
Thread t1 = new Thread(p1);
Thread t2 = new Thread(p1);
Thread t3 = new Thread(c1);
Thread t4 = new Thread(c1);
t1.start();
t2.start();
t3.start();
t4.start();
}
}
class Resource {
private String name;
private int count =1;
boolean flag = false;
Lock lock = new ReentrantLock();
Condition condition_pro = lock.newCondition();
Condition condition_con = lock.newCondition();
public void set(String name) throws Exception {
lock.lock();
try {
while(flag) {
condition_pro.await();//用while循环判断生产标记,让被唤醒的线程再次判断标记,确保不会出现生产一个而消费两个的情况
}
this.name = name + "..." + count++;
//让生产进度变慢,消费进度也随之变慢
Thread.sleep(100);
System.out.println(Thread.currentThread().getName() +" 生产 "+ this.name);
flag = true;//生产后将生产标记设为true,意为已经生产一个了,快来拿走。
condition_con.signal();//激活对方线程。
} finally {
lock.unlock();//释放锁的动作一定要执行
}
}
public void out() throws Exception {
lock.lock();
try {
while(!flag) {
condition_con.await();
}
System.out.println(Thread.currentThread().getName() +" 消费 "+ this.name);
flag = false;
condition_pro.signal();
} finally {
lock.unlock();
}
}
}
class Producer implements Runnable {
private Resource res;
public Producer(Resource res) {
this.res = res;
}
public void run() {
while(true) { //不停的生产
try {
res.set("包子");
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
class Consumer implements Runnable {
private Resource res;
public Consumer(Resource res) {
this.res = res;
}
public void run() {
while(true) {
try {
res.out();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
线程方法
让线程停止
1、直接结束run()方法。
2、如何线程调用了Object的wait()或Thread的join()、sleep()方法使线程进入阻塞状态时;
可以通过Interrupt()来终止线程的阻塞状态,从而转至就绪状态,此时会抛出InterruptedException,在对其抛出的异常处理中结束run()方法的运行。
(用毕老的一个例子来描述:一砖头拍在了一个正在打瞌睡的童鞋身上,他...受伤了...)
setDaemon(true):设置线程为守护线程(也可将其称为后台线程)。
当正在运行的线程都是守护线程时,jvm即退出。
该方法必须在启动线程前调用。
前台线程和后台线程:在执行过程中没有区别(同样抢占cpu的执行权),只在结束时有区别。
守护线程依赖于主线程;当主线程结束后,所有守护线程自动结束
主线程是前台线程。
join(),临时加入线程;当A线程执行到了B线程的B.join()方法,A线程就等待,直到B线程结束后,A线程才从阻塞状态回到就绪状态。
一般使用方式:当满足一定的条件时,让某一线程加入进来。
这里的main是线程组名(Thread-1由main开启),5是线程优先级(抢CPU的频率,默认为5,共10级),Thread-1是当前线程。
ThreadGroup,可以让程序员自己创建线程组。(几乎用不到)
setPriority(Thread.MAX_PRIORITY)设置优先级最高
Thread.yield():暂停当前正在执行的线程对象,并执行其他线程。
如果有两个线程操作同一段代码,代码中加入Thread.yield(),运行的效果类似是a线程执行一次b线程执行一次,均匀交替的执行。
何时使用线程?
当某些代码需要同时执行时,就用线程进行封装。
class MultiThread {
public static void main(String[] args) {
//第一个线程
new Thread() {
public void run() {
for(int i=0; i<1000; i++) {
System.out.println(Thread.currentThread().getName() + " " + i);
}
}
}.start();
//第二个线程
Runnable r = new Runnable() {
public void run() {
for(int i=0; i<1000; i++) {
System.out.println(Thread.currentThread().getName() + " " + i);
}
}
};
new Thread(r).start();
//第三个线程(依附主线程)
for(int i=0; i<1000; i++) {
System.out.println(Thread.currentThread().getName() + " " + i);
}
}
}
-----------------------
android培训、
java培训、期待与您交流! ----------------------
详情请查看: