对线程的一点认识

线程

1.创建线程

Java使用下列类/接口实现线程功能:

Runnable接口

Thread

ThreadGroup

例如使用Runnable接口实现线程功能:

创建的线程类

Class WorkThread implements Runnable{

Public void run(){

....................

}

}

在创建这个线程类之后,需要创建Thread类的实例,然后将WorkThread类的对象作为参数传递给Thread类的对象作为参数传递给Thread类的构造函数,如下:

Thread t = new Thread(new WorkThread())

创建的线程会执行WorkThread类定义的run方法。当run方法执行完之后,此线程就死亡了,并且不能被重新安排另一次运行。

另外可以通过子类化Thread类来创建线程:

Class WorkerThread extends Thread{

  Public void run(){

................

}

}

通过重写run方法来实现自己期望的功能。

Thread t = new WorkerThread();

T.start();

在线程启动时,并不意味着线程会立即获得CPU。相反,线程会被放入准备运行队列中,之前讨论过,线程最终会获得CPU时间片来执行。

注意

实现Runnable接口被认为是一种面向对象的方法,这种方法被推荐使用,优于通过子类化Thread类的方法。而且,如果你的类已经扩展了其他类,就不允许同时扩展Thread类。

ThreadGroup类允许将所有逻辑上相关的线程归到组中,这样你就能同时改变属于某个组的所有线程。默认情况下:创建的所有线程属于同一个组。然而,也可以创建自己的其他组,然后向其中添加新创建的线程。线程组可能包含其他的线程组,因此,可以为线程构建树型层次结构。

1.新建状态

线程在已经利用new关键字创建但是还未执行的这段时间里,处于一种特殊的新建状态中,此时,线程对象已经被分配了内存空间,私有数据已经被初始化,但是该线程尚未被调度。此时的线程可以被调度,变成可运行状态,也可以被杀死,变成死亡状态。

2.就绪状态

在处于创建状态的线程中调用start()方法将线程的状态转换为就绪状态。这是,线程已经得到除CPU时间之外的其他系统资源,只等JVM的线程调度器按照线程的优先级对该线程进行调度,从而使该线程拥有能够获得CPU时间片的机会

3.运行状态表明线程正在运行,该线程已经拥有了对CPU的控制权。这个线程一直运行到运行完毕,除非该线程主动放弃CPU的控制权或者CPU的控制权被优先级更高的线程抢占,处在运行状态的线程在下列情况下将让出CPU的控制权:

(1)线程运行完毕;

(2)有比当前线程优先级更高的线程进入可运行状态;

(3)线程主动睡眠一段时间;

(4)线程在等待某一资源。

4.如果一个线程处于挂起状态,那么这个线程暂时无法进入就绪队列。处于挂起状态的线程通常需要由某些事件才能唤醒,至于由什么事件唤醒该线程,则取决于其挂起的原因。处于睡眠状态的线程必须被挂起一段固定事件,当睡眠事件结束时就可以变成运行状态;因等待资源或消息而被挂起的线程则需要由一个外来事件唤醒。

5.正常情况下run()返回使得线程死亡。调用stop()destroy()亦有同样的效果,但是不被推荐,因为前者会产生异常,后者是强制终止,不会释放内存。

方法

        说明

有效状态

目标状态

Stat()

开始执行线程

新建

运行

Stop()

终止执行线程

运行

死亡

Sleep()

睡眠指定毫秒长的时间

运行

就绪

Sleep()

睡眠指定长的时间,允许纳秒级控制

运行

就绪

Suspend()

挂起线程

运行

就绪

Resume()

继续执行

就绪

运行

Yield()

暂时释放线程

运行

运行

Join()

调用该方法的线程转为挂起状态,直到本线程进入死亡状态才再次进入可运行状态

运行

挂起

线程同步是指java避免多个线程同时访问一个数据而造成数据混乱的方法。它可以避免多个线程同时访问相同的数据时,产生线程之间的争抢,避免一个线程刚生成的数据又会被其他线程生成的数据所覆盖。

Java用监听器的手段来完成线程的同步,就好像监听器把受保护的资源外面添加了一把锁,而这把锁只有一把钥匙。每个线程只有在得到这把钥匙之后才可以对被保护的资源执行操作,而其他的线程只能等待,直到能拿到这把钥匙。

方法同步:

一个类中任何方法都可以设计成为synchronized方法,以防止多线程数据崩溃。当一个线程进入synchronized方法后,能保证在其他任何线程访问这个方法之前完成自己的一次执行。如果一个线程试图访问一个已经启动的synchronized方法,则这个线程必须等待,直到已启动线程执行完毕,释放这个synchronized方法后才能访问。

例子:

public class DepositThread implements Runnable{

Account acc;

private static int NUM_OF_THREAD = 100;

static Thread[] threads = new Thread[NUM_OF_THREAD];

public DepositThread(Account acc){

this.acc = acc;

}

@Override

public void run() {

// TODO Auto-generated method stub

acc.deposit(20.0f);

acc.withdraw(10.0f);

}

public static void main(String[] args) {

final Account acc = new Account("王红", 1000.0f);

for(int i = 0; i < NUM_OF_THREAD; i++){

DepositThread my = new DepositThread(acc);

threads[i] = new Thread(my);

threads[i].start();

}

for(int i = 0; i < NUM_OF_THREAD; i++){

try {

threads[i].join();

catch (InterruptedException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

}

System.out.println("完成,王红的账户余额为:" + acc.getBalance());

}

}

class Account{

String name;

float amount;

public Account(String name, float amount){

this.name = name;

this.amount = amount;

}

public synchronized void deposit(float amt){

float tmp = amount;

tmp += amt;

try {

Thread.sleep(1);

catch (InterruptedException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

amount = tmp;

}

public synchronized void withdraw(float amt){

float tmp = amount;

tmp -= amt;

try {

Thread.sleep(1);

catch (InterruptedException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

amount = tmp;

}

public float getBalance(){

return amount;

}

}

方法同步和对象同步有同样的效果。

例如:

Public synchronized void yourMethod(){

...................

}

与下面代码效果相同

Public void yourMethod(){

Synchronized(this){

........

}

}

区别:有时,一个方法执行时间很长,而其中只有很短的一段时间访问关键数据,在这种情况下,将整个方法声明为synchronized,将导致其他线程因无法调用该线程的synchronized方法进行操作而长时间无法继续执行,这在整体效率上是不划算的。此时,就可以使用对象同步,只把访问关键数据的代码段用花括号括起来,在其前加上synchronized(this)即可。

线程组的各种方法:

public class TheadGroupTest {

public void test(){

ThreadGroup tg = new ThreadGroup("test");

Thread A = new Thread(tg,"A");

Thread B = new Thread(tg, "B");

Thread C = new Thread(tg, "C");

A.setPriority(6); //设置线程A的优先级为6

C.setPriority(4); //设置线程C的优先级为4,线程B的默认为5

System.out.println("tg线程组正在活动的线程个数:" + tg.activeCount());

System.out.println("线程A的优先级是:" + A.getPriority());

//设置线程组tg的优先级,但是不能改变A,B,C各自的优先级

tg.setMaxPriority(8);

System.out.println("tg线程组的优先级是:" + tg.getMaxPriority());

System.out.println("tg线程组上一级线程组信息:" + tg.getParent());

System.out.println("线程组名称:" + tg.getName());

System.out.println("tg线程组的信息:");

tg.list(); //对每个线程组中的线程调用toString()方法,返回线程组的名称和优先级

}

public static void main(String[] args) {

TheadGroupTest ttg = new TheadGroupTest();

ttg.test();

}

}

线程之间的通信:

多线程的一个重要特点是它们之间可以互相通信,线程通信使线程之间可以相互交流和等待,可以通过经常共享的数据使线程相互交流,也可以通过线程控制方法使线程相互等待。Object类为此提供了3个方法:wait(),notify(),notifyAll()。

Wait()使当前线程处于等待状态。

Notify()唤醒等待该对象线程

NotifyAll()唤醒所有等待该对象的所有线程

典型的线程间通信建立在生产者和消费者模型上:一个线程产生输出(相当于生产产品),另一个线程使用输入(相当于消费产品),先有生产者生产,才能有消费者消费。生产者未生产之前,通知消费者等待;生产后通知消费者消费,消费者消费以后再通知生产者生产,这就是等待通知机制。

代码如下:

public class ProducerThread {

public static void main(String[] args) {

Store s = new Store();

Producer pd = new Producer(s);

Consumer cs = new Consumer(s);

new Thread(pd).start();

new Thread(cs).start();

}

}

//仓库

class Store{

int product = 0;

boolean bfull = false;   //仓库中是否有产品的判断条件,初始条件是没有产品

public synchronized void put(int product){

if(bfull){

try {

wait();

catch (InterruptedException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

}

this.product = product;

System.out.println("生产者生产的产品:" + product);

bfull = true;

notify();

}

public synchronized void get(){

if(!bfull){

try {

wait();

catch (InterruptedException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

}

System.out.println("消费者消费的产品:" + product);

bfull = false;

notify();

}

}

class Producer implements Runnable{

Store s;

public Producer(Store s){

this.s = s;

}

@Override

public void run() {

// TODO Auto-generated method stub

for(int  i = 1; i <= 10; i++){

s.put(i);

}

}

}

class Consumer implements Runnable{

Store s;

public Consumer(Store s){

this.s = s;

}

@Override

public void run() {

// TODO Auto-generated method stub

for(int i = 1; i <= 10; i++){

s.get();

}

}

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值