您的位置:首页 > 编程语言 > Java开发

Java-线程

2016-05-15 19:45 465 查看

安卓中常用的线程使用方式:

主线程负责启动工作线程,Anroid中在工作线程执行耗时操作,将操作的结果传给主线程。

一个进程中可以有多个线程,其中肯定有一个是主线程,任务的执行放在主线程,主线程之外的其它线程通常称之为工作线程。

需要思考:

1.何为线程?

2.何为工作线程?何为主线程?

3.工作线程如何将数据传给主线程?涉及到线程的通讯问题。

线程对象的创建:

1)Thread()类无参构造函数

自定义一个类继承Thread类。

重写Thread类的run方法,把自定义线程的任务代码写在run方法中。

重写run方法的目的是什么?

每个线程都有自己的任务代码,jvm创建的主线程的任务代码就是在main方法中的所有代码,自定义线程的任务代码就写在run方法中,自定义线程负责了run方法的代码。

创建Thread的子类对象,并且调用start方法开启线程。

2)Thread(Runnable task);类带参构造函数 //Runnable task任务

自定义一个类实现Runnable接口。

实现Runnable实现类对象。

创建Thread类的对象,并且把Runable实现类的对象作为实参传递。

调用Thread对象的start方法开启一个线程。

创建方式1Android案例1:

public class MainActivity extends Activity {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Log.i("TAG", "onCreate.thread.name="
+Thread.currentThread().getName());
}

/**在此方法中模拟下载的动作*/
public void onClick(View view){
Log.i("TAG", "onClick.thread.name="
+Thread.currentThread().getName());

//创建工作线程,重写run()方法
new Thread(){
//当此线程以后会执行run方法
public void run() {
try{
//让当前线程让出CPU,休眠5秒钟,主线程阻塞
Thread.sleep(5000);
Log.i("TAG","下载完成");
}catch(Exception e){
e.printStackTrace();
}
};
}.start();//start表示启动线程
//线程启动以后是否能立刻获得CPU执行,存在不确定性
//例如放鞭炮点了火,什么时候炸,操作系统说了算
}

f528
public void onPlay(View view){
Toast.makeText(this,"播放音乐",1).show();
}
}

//这样主线程不受影响


创建方式1java案例1:

public class Demo1 extends Thread{

public void run() {
for(int i=0;i<100;i++){
System.out.println("自定义线程"+i);
}
}
public static void main(String[] args) {
Demo1 demo1=new Demo1();
demo1.start();

for(int i=0;i<100;i++){
System.out.println("main线程: "+i);
}
}

}


创建方式2:

public class Demo3 implements Runnable{

public void run() {
/*System.out.println("this:"+ this);
System.out.println("当前线程:"+ Thread.currentThread());*/
for(int i = 0 ; i < 100 ; i++){
System.out.println(Thread.currentThread().getName()+":"+i);
}
}

public static void main(String[] args) {
//创建Runnable实现类的对象
Demo3 d = new Demo3();
//创建Thread类的对象, 把Runnable实现类对象作为实参传递。
Thread thread = new Thread(d,"狗娃");  //Thread类使用Target变量记录了d对象,
//调用thread对象的start方法开启线程。
thread.start();

for(int i = 0 ; i < 100 ; i++){
System.out.println(Thread.currentThread().getName()+":"+i);
}

}

/*
Thread类 的run方法

*  @Override
public void run() {
if (target != null) {
target.run();  //就相当于Runnable实现类的对象的run方法作为了Thread对象的任务代码了。
}
}
*/
}


线程通讯(协作)注意的问题:

多线程之间有共享数据集。

多线程之间要有互斥(对共享数据集加锁)。

执行对象锁的通讯方法。

避免出现死锁现象。

线程常用方法:

Thread(String name):初始化线程的名字。

setName(String name):设置线程对象名。

getName():返回线程的名字。

sleep():线程睡眠指定的毫秒数。静态的方法,哪个线程执行了sleep方法代码那么就是那个线程睡眠。

currentThread():返回当前线程的对象,该方法是一个静态的方法,注意:哪个线程调用了currentThread()代码就返回哪个线程的对象。

返回当前线程对象的优先级,默认设置的优先级是5

setPriortiy(int newPriority):设置线程的优先级,注意:虽然设置了线程的优先级,但是具体的实现取决于底层操作系统的实现(最大优先级是10,最小的是1,默认的是5)。

线程并发执行:

案例2:

主线程中创建了一个工作线程,并执行工作线程,这两个方法运行在不同的线程,这时候就都会打印,就叫做线程并发执行。

public class ThreadDemo1 {

static void doMethod1(){
while(true){
System.out.println("doMethod1");
}
}
static void doMethod2(){
while(true){
System.out.println("doMethod2");
}
}
//两个线程并发的执行,没加线程打印的时候只会打印doMethod1!
//我们让两个线程并发执行:
//此方法运行在由JVM启动的主线程中
public static void main(String[] args) {
//创建工作线程,并启动线程
new Thread(){//主线程中负责创建了一个工作线程,并启动
public void run() {
doMethod1();
};
}.start();//线程的启动调用start方法
//在主线程调用doMethod2
doMethod2();
//在main方法中有两个线程在并发执行,这样打印的时候就都会打印。
}
}


线程启动时候立刻运行存在不确定性:

案例:

public class ThreadDemo2 {
static String s;
public static void main(String[] args) {

new Thread(){
@Override
public void run() {
s="helloworld";
System.out.println("run.s="+s);
}
}.start();
//线程启动以后处于就绪状态(可运行态)
//线程启动以后是否能够立刻获得CPU执行,存在不确定性。
while(s==null);
System.out.println(s.toUpperCase());
}
}
//通过运行结果我们可以发现,主线程负责启动工作线程,线程是否立刻运行存在不确定性


线程的构造函数和无参构造函数:

1)Thread()类无参构造函数

2)Thread(Runnable task);类带参构造函数 //要new Runnable接口类型的参数

两者区别案例:

/**案例:
* 主线程中创建两个工作线程,
* 工作线程A负责从键盘输入数据,
* 工作线程B负责将工作线程A输入的数据
* 转换为大写输出。
* 要求:
* 1)工作线程A的创建借助Thread类的无
*   参构造函数
* 2)工作线程B的创建借助Thread(Runnable task)//Runnable接口
*   构造函数
* */

public class ThreadDemo3 {
static String str;
public static void main(String[] args) {
//工作线程A
new Thread(){
public void run() {
Scanner sc=new Scanner(System.in);
System.out.println("从键盘输入一个字符串:");
str=sc.nextLine();
sc.close();
};

}.start();//
//工作线程B
new Thread(new Runnable() {//target
@Override
public void run() {
while(str==null);
System.out.println(str.toUpperCase());
}
}).start();

//当工作线程B启动以后会执行runnale接口的的run方法
//底层会先调用Thread类的run方法,start后在thread类的run方法中调用了runnable接口的run方法。

}
}


线程的run()方法和线程Runable()接口的run()方法启动顺序:

1、线程启动以后首先会调用线程对象的run方法。

2、如果不重写线程对象的run()方法(也就是没有run()方法),这时候系统中线程对象的run()方法本身自动调用了Runable接口的run()方法

3、若果重写了线程对象的run()方法(也就是写了run()方法),Runnable接口对象的run方法执行不执行取决于有没有在Thread类的run方法中调用,如果没有调用就不会执行。

案例:

public class ThreadDemo4 {

static Runnable target;
public static void main(String[] args) {
target=new Runnable() {
//此方法是Runable接口类的run()方法
public void run() {
System.out.println("R.run");
}
};

new Thread(target){
//此方法是线程对象的run()方法
public void run() {

System.out.println("T.run");
}
}.start();

//1、线程启动以后首先会调用线程对象的run方法。
//2、如果不重写线程对象的run()方法(也就是没有run()方法),这时候系统中线程对象的run()方法本身自动调用了Runable接口的run()方法
//3、若果重写了线程对象的run()方法(也就是写了run()方法),Runnable接口对象的run方法执行不执行取决于有没有在Thread类的run方法中调用,如果没有调用就不会执行。

}
}


线程任务的封装:

线程执行任务

线程可以为一个对象(对象Thread类型)

任务可以封装成一个对象(对象Runnable类型)

new  Thread(){
public void run(){
任务/编写要执行的操作
}
}.start()


可以如下操作:

class Task implements Runnable{
public void run(){
//编写要执行的任务操作
}
}


线程执行任务:

new Thread(new Task()).start();


线程对象的状态及常用方法:

1) start() 启动线程

2) run() 执行业务逻辑

3) sleep() 让出cpu,处于阻塞状态,不能继续获得cpu执行

4) interuput() 唤醒正在休眠的线程

5) setDaemon() 设置线程为守护线程

6) join() 让线程插队(抢占)执行

7) yield() 让出cpu,但线程仍旧处于就绪状态

8) setName() 设置线程的名字

9) getName() 获得当前线程的名字

10) currentThread() 获得当前线程

11) isAlive() 判断线程是否还活着

12) setPriority(1/10) 设置线程的优先级,但是,有些时候设置优先级不起作用,要看操作系统,取决于操作系统

线程同步(互斥与协作):

1、多个线程谁先执行存在不确定性。

2、多线程之间有无共享数据集,有的话要考虑线程安全。

所谓线程同步指的是多个线程并发执行时在共享数据集上的互斥与协作,线程安全。

线程同步是保证线程安全的一种手段,例如多个线程同时卖共同100张票。

线程安全问题的解决方案:synchronized

出现线程安全的根本原因:

存在两个或者两个以上的线程对象,而且线程之前共享着一个资源。

有多个语句操作了共享资源。

线程同步机制:

syschronized (锁对象){
需要被同步的代码
}


同步代码块要注意的事项:

任意一个对象都可以做为锁对象。

在同步代码块中调用了sleep方法并不是释放锁对象。

只有真正存在线程安全问题的时候才使用同步代码块,否则会降低效率。

多线程操作的锁对象必须是唯一共享的,否则无效。

共享(static)目的:判断的锁对象如果不是唯一共享的,就没法判断当前锁对象的状态是开还是关。

案例:模拟3个窗口同时在售50张票 。

class SaleTicket extends Thread{

static int num = 50;//票数  不加static的非静态的成员变量,非静态的成员变量数据是在每个对象中都会维护一份数据的。

static  Object o = new Object();

public SaleTicket(String name) {
super(name);
}

public void run() {
while(true){
//同步代码块
synchronized ("锁" /**o*/) {     //用" "引起来的对象,在字符串常量池中不回再重新创建,所以能锁住。但是new String("锁")就
//不行,因为是从堆内存当中创建,就不是一个对象了
if(num>0){
System.out.println(Thread.currentThread().getName()+"售出了第"+num+"号票");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
num--;
}else{
System.out.println("售罄了..");
break;
}
}

}
}
}

public class Demo {
public static void main(String[] args) {
//创建三个线程对象,模拟三个窗口
SaleTicket thread1 = new SaleTicket("窗口1");
SaleTicket thread2 = new SaleTicket("窗口2");
SaleTicket thread3 = new SaleTicket("窗口3");
//开启线程售票
thread1.start();
thread2.start();
thread3.start();

}
}


线程中的死锁现象:

java中的同步机制解决了线程安全问题,但是也同时引发了死锁现象。

何为线程死锁?

死锁现象出现的根本原因:

存在两个或者两个以上的线程。

存在两个或者两个以上的共享资源。

互相等待对象的共享资源。

死锁的解决方案:无,只能尽量避免发生而已。

案例:开空调案例(开空调需要遥控器和电池)

class DeadLock extends Thread{

public DeadLock(String name){
super(name);
}

public void run() {
if("张三".equals(Thread.currentThread().getName())){
synchronized ("遥控器") {
System.out.println("张三拿到了遥控器,准备 去拿电池!!");
synchronized ("电池") {
System.out.println("张三拿到了遥控器与电池了,开着空调爽歪歪的吹着...");
}
}
}else if("狗娃".equals(Thread.currentThread().getName())){
synchronized ("电池") {
System.out.println("狗娃拿到了电池,准备去拿遥控器!!");
synchronized ("遥控器") {
System.out.println("狗娃拿到了遥控器与电池了,开着空调爽歪歪的吹着...");
}
}

}
}

}

public class Demo2 {

public static void main(String[] args) {
DeadLock thread1 = new DeadLock("张三");
DeadLock thread2 = new DeadLock("狗娃");
//开启线程
thread1.start();
thread2.start();
}

}


守护线程

(例如在后台偷偷的干不光彩的事情)

在一个进程中如果只剩下了守护线程,那么守护线程也会死亡。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: