-------android培训、java培训、期待与您交流!----------
1、概述
进程:是一个正在执行中的程序。每一个进程执行都有一个执行顺序,该顺序是一个执行路径,或者叫一个控制单元。
线程:进程中的一个独立的控制单元,线程在控制着进程的执行。
一个进程中至少有一个线程。
Java VM 启动的时候会有一个进程java.exe。该进程中至少有一个线程负责Java程序的执行。而且这个线程运行的代码存在于main方法中。该线程称之为主线程。
2、继承Thread类创建线程
/*
*创建线程的第一种方式:继承Thread类。步骤:
*1、定义类并继承Thread
*2、重写Thread类中的run方法。
* 目的:将自定义的代码存储在run方法中,让线程运行。
*3、调用线程的start方法:该方法两个作用:启动线程,调用run方法。
*/
class CreateThread extends Thread{
/*
* 为什么要覆盖run方法呢?
* Thread类用于描述线程。
* 该类定义了一个功能,用于存储线程要运行的代码,该存储功能就是run方法。
* 也就是说Thread类中的run方法,用于存储线程要运行的代码。
*/
public void run(){
for(int i = 0; i < 60; i++){
P.rintln("我是run() -- " + i);
}
}
}
3、run和start的区别
public static void testMultiThread1(){
/*
* 发现运行结果每一次都不同。因为多个线程都获取cpu的执行权。cpu执行到谁,谁就运行。
* 明确一点,在某一个时刻,只能有一个线程在运行(多核除外)。
* cpu在做着快速的切换,以达到看上去是同时运行的效果。我们可以形象把多线程的运行行为称为在互相抢夺cpu的执行权。
* 这就是多线程的一个特性:随机性。谁抢到谁执行,至于执行多长,cpu说了算。
*/
CreateThread ct = new CreateThread(); //new一个对象其实就是创建好一个线程
ct.start(); //启动线程,调用run()。
// ct.run(); //run()不启动线程,只是执行run中的代码,因此会将run中的输出全部打印之后才执行下面的代码
for(int i = 0; i < 60; i++){
P.rintln("Hello World! -- " + i);
}
}
4、实现Runnable接口创建线程、多线程的安全及同步函数
/**
* 黑马入学学习视频之多线程:实现Runnable接口,多线程的安全,同步函数
*
* 创建线程的第二种方式:实现Runnable接口。步骤:
* 1、定义类实现Runnable接口
* 2、覆盖Runnable接口中的run方法
* 将线程要运行的代码存放在该run方法中。
* 3、通过Thread类建立线程对象。
* 4、将Runnable接口的子类对象作为实际参数传递给Thread类的构造函数。
* 自定义的run方法所属的对象是Runnable接口的子类对象。
* 所以要让线程去运行指定对象的run方法,就必须明确该run方法所属对象。
* 5、调用Thread类的start方法启动线程并调用实现Runnable接口子类的run方法。
*
* 实现方式和继承方式的区别:
* 实现方式好处:避免了单继承的局限性。在定义线程时,建议使用实现方式。
* 继承Thread:线程代码存放在Thread子类run方法中。
* 实现Runnable:线程代码存放在接口的子类的run方法。
*/
public class MultiThread_03_ImpleRunnable {
public static void main(String[] args){
ticket();
}
public static void bank(){
Cus c = new Cus();
Thread t1 = new Thread(c);
Thread t2 = new Thread(c);
t1.start();
t2.start();
}
public static void ticket(){
//使用此种方法创建多线程并启动只共用一个tick
Ticket2 t = new Ticket2();
Thread t1 = new Thread(t);
Thread t2 = new Thread(t);
Thread t3 = new Thread(t);
t1.start();
t2.start();
t3.start();
}
}
/*
* 通过分析,发现打印出0、-1等错票。多线程的运行出现了安全问题。
* 问题的原因:
* 当多条语句在操作同一个线程共享数据时,一个线程对多条语句只执行了一部分,
* 还没有执行完,另一个线程参与进来执行,导致共享数据出错。
* 解决办法:
* 对多条操作共享数据的语句,只能让一个线程都执行完。在执行过程中,其他线程不可以参与执行。
*
* Java对于多线程的安全问题提供了专业的解决方式。就是同步代码块:
* synchronized(对象){
* 需要被同步的代码
* }
* 对象如同锁,持有锁的线程可以在同步中执行。没有持有锁的线程即使获取cpu的执行权,也进不去,因为没有锁。
*
* 同步的前提:
* 1、必须要有两个或者两个以上的线程。
* 2、必须是多个线程使用同一个锁
* 必须保证同步中只能有一个线程运行。
*
* 好处:解决了多线程的安全问题
* 弊端:判断锁消耗了更多的资源
*/
class Ticket2 implements Runnable{
private int tick = 100; //设置为静态的,所有线程共用一个tick
Object obj = new Object();
public void run(){
while(true){
synchronized(obj){
if(tick > 0){
//run方法是重写的接口的方法,接口方法没有抛异常,因此这里的异常只能获取,不能抛出。
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
//没有继承Thread类,需要使用Thread.currentThread()方法获取当前运行的线程
P.rintln(Thread.currentThread().getName() + " sale : " + tick--);
}
}
}
}
}
/*
* 需求:银行有一个金库,有两个储户分别存300元,每次存100,存3次。
* 如何找多线程安全问题:
* 1、明确哪些代码是多线程运行代码
* 2、明确共享数据
* 3、明确多线程运行代码中哪些语句是操作共享数据的。
* 只同步需要同步的代码。
*/
class Bank{
private int sum;
Object obj = new Object();
//使用synchronized修饰的函数具有同步功能
public synchronized void add(int n){
// synchronized(obj){
sum = sum + n;
//此处有可能出现异常
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
P.rintln(Thread.currentThread() + "sum = " + sum);
// }
}
}
class Cus implements Runnable{
private Bank b = new Bank();
public void run(){
for(int x = 0; x < 3; x++){
b.add(100);
}
}
}
class Ticket1 extends Thread{
private static int tick = 100; //当继承Thread时,设置为静态的,所有线程共用一个tick
public void run(){
while(tick > 0){
//继承自Thread,可以直接使用currentThread()方法获取当前执行的线程
P.rintln(currentThread().getName() + " sale : " + tick--);
}
}
}
class Ticket3 implements Runnable{
private int tick = 100;
Object obj = new Object();
boolean flag = true;
public void run(){
// if(flag){
// while(true){
// synchronized(obj){
// if(tick > 0){
// try{
// Thread.sleep(5);
// } catch(InterruptedException e){}
//
// P.rintln(Thread.currentThread() + " obj-sale : " + tick--);
// }
// }
// }
// }
// else{
// while(true){
// show();
// }
// }
while(true){
synchronized(this){
if(tick > 0){
try{
Thread.sleep(5);
} catch(InterruptedException e){}
P.rintln(Thread.currentThread() + " obj-sale : " + tick--);
}
}
show();
}
}
/*
* 同步函数用的是哪一个锁:
* 函数需要被对象调用,那么函数都有一个所属对象引用。就是this。所以同步函数使用的锁是this。
*/
public synchronized void show(){
if(tick > 0){
try{
Thread.sleep(5);
} catch(InterruptedException e){}
P.rintln(Thread.currentThread() + " show-sale : " + tick--);
}
}
}
静态进内存时,内存中没有本类对象,但是一定有该类对应的字节码文件对象:类名.class,该对象的类型就是Class。
当同步函数被static修饰后,使用的锁是该方法所在类的字节码文件对象:类名.class。
5、单例设计模式
/**
* 黑马入学学习视频之多线程:单例设计模式
* 所谓“懒汉式”与“饿汉式”的区别,是在建立单例对象的时间不同。
* @author xiaogh
*/
public class MultiThread_04_Singleton {
public static void main(String[] args) {
HungrySingleton h1 = HungrySingleton.getInstance();
HungrySingleton h2 = HungrySingleton.getInstance();
P.rintln(h1 == h2);
SafeLazySingleton s1 = SafeLazySingleton.getInstance();
SafeLazySingleton s2 = SafeLazySingleton.getInstance();
P.rintln(s1 == s2);
}
}
/**
* 单例设计模式:饿汉式
* “饿汉式”是不管你用不用的上,一开始就创建这个单例对象
*/
class HungrySingleton{
//static修饰符,jvm保证single只能被初始化一次,加上final修饰符使程序更加严谨
private static final HungrySingleton single = new HungrySingleton();
//组织外部使用new实例化对象
private HungrySingleton(){}
//获取实例,返回唯一的single对象
public static HungrySingleton getInstance(){
return single;
}
}
/**
* 单例设计模式:懒汉式
* “懒汉式”是在你真正用到的时候才去创建这个单例对象。即延迟加载。
* “懒汉式”在多线程访问时会出现安全隐患
* @author xiaogh
*/
class LazySingleton{
private static LazySingleton single = null;
private LazySingleton(){}
public static LazySingleton getInstance(){
if(single == null){
//若第一个进来的线程在此处丢失执行权,进入阻塞状态,会使其他线程进来,这样子进来的所有线程会创建不同的实例
single = new LazySingleton();
}
return single;
}
}
/**
* 使用同步锁解决“懒汉式”的多线程安全问题
* @author xiaogh
*/
class SafeLazySingleton{
private static SafeLazySingleton single = null;
private SafeLazySingleton(){}
public static SafeLazySingleton getInstance(){
if(single == null){ //外层嵌套一层判断,减少获取锁的次数,
synchronized(SafeLazySingleton.class){ //使用该类的class字节码对象作为同步锁
if(single == null){
single = new SafeLazySingleton();
}
}
}
return single;
}
}
6、等待唤醒机制
wait(),notify(),notifyAll()都使用在同步中。
因为要对持有监视器(锁)的线程操作,所以要使用在同步中,因为只有同步才具有锁。
为什么这些操作线程的方法要定义在Object中呢?因为这些方法在操作同步中线程时,都必须要标识它们所操作线程持有的锁,
只有同一个锁上的被等待线程,可以被同一个锁上notify唤醒。不可以对不同锁中的线程进行唤醒。
也就是说,等待和唤醒必须是同一个锁。而锁可以是任意对象,所以可以被任意对象调用的方法定义在Object类中
/**
* 下面这个例子演示多线程等待唤醒机制
* @author xiaogh
*/
class Source1{
public String name;
public String sex;
public boolean flag;
}
class Input1 implements Runnable{
private Source1 source;
Input1(Source1 source){
this.source = source;
}
public void run(){
boolean flag = true;
while(true){
synchronized(source){
if(source.flag)
try {
source.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
if(flag){
source.name = "jake";
source.sex = "male";
}
else{
source.name = "小莉";
source.sex = "女女女";
}
flag = !flag;
source.flag = true;
source.notify();
}
}
}
}
class Output1 implements Runnable{
private Source1 source;
public Output1(Source1 source){
this.source = source;
}
public void run(){
while(true){
synchronized(source){
if(!source.flag)
try {
source.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
P.rintln(source.name + " --- " + source.sex);
source.flag = false;
source.notify();
}
}
}
}
7、生产消费者模式
使用synchronized控制多线程安全
public class MultiThread_08_ProducerConsumer {
public static void main(String[] args){
Resource res = new Resource();
Thread t1 = new Thread(new Producer(res));
Thread t2 = new Thread(new Consumer(res));
Thread t3 = new Thread(new Producer(res));
Thread t4 = new Thread(new Consumer(res));
t1.start();
t2.start();
t3.start();
t4.start();
}
}
class Resource{
private String name;
private int count = 1;
private boolean flag = false;
public synchronized void set(String name){
while(flag){ //使用while循环判断,可以在notiy之后再次判断flag标记,避免重复生产
try{
this.wait(); //this可写可不写
}catch(InterruptedException e){
e.printStackTrace();
}
}
this.name = name + "--" + count++;
P.rintln(Thread.currentThread().getName() + "---生产者-----------" + this.name);
flag = true;
this.notifyAll(); //将所有线程唤醒,使用notify有可能依然唤醒生产者线程,导致所有线程wait。
}
public synchronized void out(){
while(!flag){
try{
this.wait();
}catch(InterruptedException e){
e.printStackTrace();
}
}
P.rintln(Thread.currentThread().getName() + "...消费者..." + this.name);
flag = false;
this.notifyAll();
}
}
//生产者
class Producer implements Runnable{
private Resource res;
public Producer(Resource res){
this.res = res;
}
public void run(){
while(true){
this.res.set("产品");
}
}
}
//消费者
class Consumer implements Runnable{
private Resource res;
public Consumer(Resource res){
this.res = res;
}
public void run(){
while(true){
this.res.out();
}
}
}
JDK5.0提供了多线程的升级解决方案,将同步synchronized替换成显式Lock操作。
将Object中的wait、notify、notifyAll替换成Condition对象,该对象可以使用Lock锁进行获取。
下面的示例中,实现了本方只唤醒对方操作。
class Resource2{
private String name;
private int count = 1;
private boolean flag = false;
private Lock lock = new ReentrantLock();
private Condition condition_p = lock.newCondition(); //生产者的锁对象
private Condition condition_c = lock.newCondition(); //消费者的锁对象
public void set(String name) throws InterruptedException{
lock.lock();
try{
while(flag){
condition_p.await();
}
this.name = name + "--" + count++;
P.rintln(Thread.currentThread().getName() + "---生产者-----------" + this.name);
flag = true;
condition_c.signal();
}
finally{ //将释放锁的动作放入到finally中
lock.unlock();
}
}
public void out() throws InterruptedException{
lock.lock();
try{
while(!flag){
condition_c.await();
}
P.rintln(Thread.currentThread().getName() + "...消费者..." + this.name);
flag = false;
condition_p.signal();
}
finally{
lock.unlock();
}
}
}
//生产者
class Producer2 implements Runnable{
private Resource2 res;
public Producer2(Resource2 res){
this.res = res;
}
public void run(){
while(true){
try{
this.res.set("产品");
}
catch(InterruptedException e){
e.printStackTrace();
}
}
}
}
//消费者
class Consumer2 implements Runnable{
private Resource2 res;
public Consumer2(Resource2 res){
this.res = res;
}
public void run(){
while(true){
try{
this.res.out();
}
catch(InterruptedException e){
e.printStackTrace();
}
}
}
}
8、停止线程
stop方法已过时。
如何停止线程:只有一种,run方法结束。
开启多线程运行,运行代码通常是循环结构,只要控制住循环,就可以让run方法结束,也就是线程结束。
特殊情况:当线程处于冻结状态,就不会读取到标记,那么线程就不会结束。
当没有指定的方式让冻结的线程恢复到运行状态时,这时需要对冻结进行清除。
强制让线程恢复到运行状态中来,这样就可以操作标记让线程结束。Thread类提供该方法:interrupt()。
public class MultiThread_10_InterruptThread {
public static void main(String[] args){
InterruptThread it = new InterruptThread();
Thread t1 = new Thread(it);
Thread t2 = new Thread(it);
t1.start();
t2.start();
int num = 0;
while(true){
if(num++ == 60){
// it.change();
t1.interrupt();
t2.interrupt();
break;
}
P.rintln(Thread.currentThread().getName() + "....run." + num);
}
}
}
class InterruptThread implements Runnable{
private boolean flag = true;
public synchronized void run(){
while(flag){
try{
wait();
}
catch(InterruptedException e){
P.rintln(Thread.currentThread().getName() + ".....Exception");
this.flag = false;
}
P.rintln(Thread.currentThread().getName() + "....run");
}
}
public void change(){
this.flag = false;
}
}
9、join方法、优先级和yield方法
public class MultiThread_11_JoinYieldPriority {
public static void main(String[] args) throws InterruptedException{
yield();
}
public static void yield(){ //临时停止
JoinDemo join = new JoinDemo();
Thread t1 = new Thread(join);
Thread t2 = new Thread(join);
t1.start();
t2.start();
}
public static void priority(){
JoinDemo join = new JoinDemo();
Thread t1 = new Thread(join);
Thread t2 = new Thread(join);
t1.start();
t1.setPriority(Thread.MAX_PRIORITY); //设置优先级
t2.start();
t2.setPriority(Thread.MIN_PRIORITY);
for(int i = 0; i < 80; i++){
P.rintln(Thread.currentThread().toString() + "..." + i); //toString方法可以显示线程名称、优先级、所属线程组
}
P.rintln("main over");
}
//当A线程执行到了B线程的join方法时,A就会等待,等B线程都执行完,A才会执行。
//join用来临时加入线程执行。
public static void join() throws InterruptedException{
JoinDemo join = new JoinDemo();
Thread t1 = new Thread(join);
Thread t2 = new Thread(join);
t1.start();
// t1.join(); //主线程需要等待t1执行完才会继续执行
t2.start();
t1.join(); //主线程需要等待t1执行完才会继续执行
for(int i = 0; i < 80; i++){
P.rintln("main....." + i);
}
P.rintln("main over");
}
}
class JoinDemo implements Runnable{
public void run(){
for(int i = 0; i < 70; i++){
P.rintln(Thread.currentThread().toString() + "......" + i);
Thread.yield(); //释放执行权
}
}
}
-------
android培训、
java培训、期待与您交流! ----------详细请查看:
www.itheima.com