1.
public class SimpleThread extends Thread{
private int countDown = 5;
private int threadNumber;
private static int threadCount = 0;
public SimpleThread() {
threadNumber = ++threadCount;
System.out.println("Making "+threadNumber);
}
public void run() {
while(true) {
System.out.println("Thread "+threadNumber+"("+countDown+")");
if(--countDown == 0)
return;
}
}
public static void main(String[] args) {
// TODO Auto-generated method stub
for(int i = 0; i < 5; i++)
new SimpleThread().start();
System.out.println("All Threads Started");
}
}
Thread包含了一个特殊的方法,start(),它的作用是对线程进行特殊的初始化,然后调用run().
调用构建器来构建对象,然后用start()配置线程,再调用run()。如果不调用start(),线程便永远不会启动。
cpu处理一个现有线程集的顺序是不确定的——除非我们亲自介入,并用Thread的setPriority()方法调整它们的优先级。
线程会注册自己,所以某处实际存在着对它的一个引用。这样一来,垃圾收集器便只好对它“瞠目以对”了。
一旦用户按下Start按钮,线程就会启动,但马上结束线程的创建。这样一来,尽管线程仍在运行,但程序的主要工作却能得以继续。
当按下onOff按钮时,几乎立即能得到正确的响应。当然,这个响应其实并不是"立即"发生的。只有线程拥有cpu的执行时间,并注意到标记已发生改变,计数器才会停止。
2.
import java.awt.*;
import java.awt.event.*;
import java.applet.*;
class Ticker extends Thread {
private Button b = new Button("Toggle");
private TextField t = new TextField(10);
private int count = 0;
private boolean runFlag = true;
public Ticker(Container c) {
b.addActionListener(new ToggleL());
Panel p = new Panel();
p.add(t);
p.add(b);
c.add(p);
}
class ToggleL implements ActionListener {
public void actionPerformed(ActionEvent e) {
runFlag = !runFlag;
}
}
public void run() {
while(true) {
if(runFlag)
t.setText(Integer.toString(count++));
try {
sleep(100);
}catch(InterruptedException e) {
}
}
}
}
public class Counter4 extends Applet{
private Button start = new Button("Start");
private boolean started = false;
private Ticker[] s;
private boolean isApplet = true;
private int size;
public void init() {
if(isApplet)
size = Integer.parseInt(getParameter("size"));//程序片框架会进行必要的启动工作,以便进入init()前收集好一些参数
s = new Ticker[size];
for(int i = 0; i < s.length; i++)
s[i] = new Ticker(this);
start.addActionListener(new StartL());
add(start);
}
class StartL implements ActionListener {
public void actionPerformed(ActionEvent e) {
if(!started) {
started = true;
for(int i = 0; i < s.length; i++)
s[i].start();
}
}
}
public static void main(String[] args) {
// TODO Auto-generated method stub
Counter4 applet = new Counter4();
applet.isApplet = false;
applet.size = (args.length == 0 ? 5 : Integer.parseInt(args[0]));
Frame aFrame = new Frame("Counter4");
aFrame.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
aFrame.add(applet, BorderLayout.CENTER);
aFrame.setSize(200, applet.size*50);
applet.init();
applet.start();
aFrame.setVisible(true);
}
}
3.
Daemon线程的作用是在程序的运行期间于后台提供一种“常规”服务,但它并不属于程序的一个基本部分。通过调用isDaemon(),可调查一个线程是不是一个Daemon。而且能用setDaemon()打开或关闭一个线程的Daemon状态。
4.
import java.awt.*;
import java.awt.event.*;
import java.applet.*;
class TwoCounter extends Thread {
private boolean started = false;
private TextField t1 = new TextField(5),
t2 = new TextField(5);
private Label l = new Label("count1 == count2");
private int count1 = 0, count2 = 0;
public TwoCounter(Container c) {
Panel p = new Panel();
p.add(t1);
p.add(t2);
p.add(l);
c.add(p);
}
public void start() {
if(!started)
started = true;
super.start();
}
public void run() {
while(true) {
t1.setText(Integer.toString(count1++));
t2.setText(Integer.toString(count2++));
try {
sleep(500);
}catch(InterruptedException e) {
}
}
}
public void synchTest() {
Sharing1.incrementAccess();
if(count1 != count2)
l.setText("Unsynched");
}
}
class Watcher extends Thread {
private Sharing1 p;
public Watcher(Sharing1 p) {
this.p = p;
start();
}
public void run() {
while(true) {
for(int i = 0; i < p.s.length; i++)
p.s[i].synchTest();
try{
sleep(500);
}catch(InterruptedException e) {
}
}
}
}
public class Sharing1 extends Applet{
TwoCounter[] s;
private static int accessCount = 0;
private static TextField aCount = new TextField("0", 10);
public static void incrementAccess() {
accessCount++;
aCount.setText(Integer.toString(accessCount));
}
private Button start = new Button("Start"),
observer = new Button("Observe");
private boolean isApplet = true;
private int numCounters = 0;
private int numObservers = 0;
public void init() {
if(isApplet) {
numCounters = Integer.parseInt(getParameter("size"));
numObservers = Integer.parseInt(getParameter("observers"));
}
s = new TwoCounter[numCounters];
for(int i = 0; i < s.length; i++)
s[i] = new TwoCounter(this);
Panel p = new Panel();
start.addActionListener(new StartL());
p.add(start);
observer.addActionListener(new ObserverL());
p.add(observer);
p.add(new Label("Access Count"));
p.add(aCount);
add(p);
}
class StartL implements ActionListener {
public void actionPerformed(ActionEvent e) {
for(int i = 0; i < s.length; i++)
s[i].start();
}
}
class ObserverL implements ActionListener {
public void actionPerformed(ActionEvent e) {
for(int i = 0; i < numObservers; i++)
new Watcher(Sharing1.this);
}
}
public static void main(String[] args) {
// TODO Auto-generated method stub
Sharing1 applet = new Sharing1();
applet.isApplet = false;
applet.numCounters = (args.length == 0 ? 5 : Integer.parseInt(args[0]));
applet.numObservers = (args.length < 2 ? 5 : Integer.parseInt(args[1]));
Frame aFrame = new Frame("Sharing1");
aFrame.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
aFrame.add(applet, BorderLayout.CENTER);
aFrame.setSize(350, applet.numCounters*100);
applet.init();
applet.start();
aFrame.setVisible(true);
}
}
t1.setText(Integer.toString(count1++));
t2.setText(Integer.toString(count2++));
在运行的时候,会发现count1和count2被“观察”的次数是不相等的。这是由线程的本质造成的——它们可在任何时候挂起(暂停).所以在执行上述两行的时候,有时会出现执行暂停现象。同时,watcher线程也正好跟随进来,并正好在这个时候进行比较,造成计数器出现不相等的情况。
使用线程时一个基本的问题。我们无从知道一个线程什么时候运行。想象自己坐在一张桌子前面,桌上有一把叉子,准备插起自己的最后一块食物。当叉子要碰到食物时,食物却突然消失了(因为这个线程已被挂起,同时另一个线程进来偷走了食物)。这便是我们要解决的问题。
5.
每个对象都包含了一把锁(也叫做“监视器”),它自动称为对象的一部分(不必为此写任何特殊的代码)。调用synchronized方法时,对象就会锁定,不可再调用那个对象的其他任何synchronized方法,除非第一个方法完成了自己的工作,并解除锁定。
一个特定对象的所有synchronized方法都共享着一把锁,而且这把锁能防止多个方法对通用内存进行写操作。
每个类也有自己的一把锁(作为类的Class对象的一部分)。所以synchronized static 方法可在一个类的范围内被相互间锁起来,防止与static数据的接触。
6.
import java.awt.*;
import java.awt.event.*;
import java.applet.*;
class TwoCounter2 extends Thread {
private boolean started = false;
private TextField t1 = new TextField(5),
t2 = new TextField(5);
private Label l = new Label("count1 == count2");
private int count1 = 0, count2 = 0;
public TwoCounter2(Container c) {
Panel p = new Panel();
p.add(t1);
p.add(t2);
p.add(l);
c.add(p);
}
public void start() {
if(!started)
started = true;
super.start();
}
public synchronized void run() {
while(true) {
t1.setText(Integer.toString(count1++));
t2.setText(Integer.toString(count2++));
try {
sleep(500);
}catch(InterruptedException e) {
}
}
}
public synchronized void synchTest() {
Sharing1.incrementAccess();
if(count1 != count2)
l.setText("Unsynched");
}
}
class Watcher2 extends Thread {
private Sharing2 p;
public Watcher2(Sharing2 p) {
this.p = p;
start();
}
public void run() {
while(true) {
for(int i = 0; i < p.s.length; i++)
p.s[i].synchTest();
try{
sleep(500);
}catch(InterruptedException e) {
}
}
}
}
public class Sharing2 extends Applet{
TwoCounter2[] s;
private static int accessCount = 0;
private static TextField aCount = new TextField("0", 10);
public static void incrementAccess() {
accessCount++;
aCount.setText(Integer.toString(accessCount));
}
private Button start = new Button("Start"),
observer = new Button("Observe");
private boolean isApplet = true;
private int numCounters = 0;
private int numObservers = 0;
public void init() {
if(isApplet) {
numCounters = Integer.parseInt(getParameter("size"));
numObservers = Integer.parseInt(getParameter("observers"));
}
s = new TwoCounter2[numCounters];
for(int i = 0; i < s.length; i++)
s[i] = new TwoCounter2(this);
Panel p = new Panel();
start.addActionListener(new StartL());
p.add(start);
observer.addActionListener(new ObserverL());
p.add(observer);
p.add(new Label("Access Count"));
p.add(aCount);
add(p);
}
class StartL implements ActionListener {
public void actionPerformed(ActionEvent e) {
for(int i = 0; i < s.length; i++)
s[i].start();
}
}
class ObserverL implements ActionListener {
public void actionPerformed(ActionEvent e) {
for(int i = 0; i < numObservers; i++)
new Watcher2(Sharing2.this);
}
}
public static void main(String[] args) {
// TODO Auto-generated method stub
Sharing2 applet = new Sharing2();
applet.isApplet = false;
applet.numCounters = (args.length == 0 ? 5 : Integer.parseInt(args[0]));
applet.numObservers = (args.length < 2 ? 5 : Integer.parseInt(args[1]));
Frame aFrame = new Frame("Sharing2");
aFrame.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
aFrame.add(applet, BorderLayout.CENTER);
aFrame.setSize(350, applet.numCounters*100);
applet.init();
applet.start();
aFrame.setVisible(true);
}
}
无论run()还是synchTest()都是"同步的".如果只同步其中的一个方法,那么另一个就可以自由忽视对象的锁定,并可无碍地调用。所以必须记住一个重要准则:对于访问某个关键共享资源的所有方法,都必须把它们设为synchronized,否则就不能正常工作。
由于肯定要为每个对象运行run(),所以锁永远不能打开,而synchTest()永远不会得到调用。
为解决这个问题,我们能采取的一个办法是只将run()中的一部分代码隔离出来。用这个办法隔离出来的那部分代码叫做"关键区域"。
我们用synchronized关键字指出对象的锁用于对其中封闭的代码进行同步。
6.
一个线程可以有四种状态:
(1):线程对象已经创建,但尚未启动,所以不可运行.新(New).
(2).可运行(Runnable):意味着一旦时间分片机制有空闲的cpu周期提供给一个线程,那个线程便可立即开始运行。因此,线程可能在,也可能不在运行中,但一旦条件许可。。
(3)死(Dead):从自己的run()方法返回后,一个线程便已死掉。亦可调用stop()令其死掉,但会产生一个违例。
(4)赌塞(Blocked):线程可以运行,但有某种东西阻碍了它。若线程处于堵塞状态,调度机制可以简单的跳过它,不给它分配任何cpu时间。
7.
堵塞的原因:
(1)调用sleep(毫秒数),使线程进入睡眠状态。
(2)调用suspend()暂停了线程的执行。除非线程收到resume()消息,否则不会返回"可运行"状态。
(3)用wait()暂停了线程的执行。除非线程收到notify()或者notifyAll()消息,否则不会变成“可运行”。
(4)线程正在等候一些IO操作完成。
(5)线程试图调用另一个对象的“同步”方法,但那个对象处于锁定状态。
亦可调用yield()(Thread类的一个方法)自动放弃cpu,以便其他线程能够运行。
8.
无论sleep()还是suspend()都不会在自己被调用的时候接触锁定。wait()方法在被调用的时候却会解除锁定,这意味着可在执行wait()期间调用线程对象中的其他同步方法。
wait()在挂起内部调用时,会解除对象锁定。
wait()和notify()是基础类Object的一部分,因为他们操纵的对象锁也属于每个对象的一部分。
我们能调用wait()的唯一地方是在同一个同步的方法或代码块内部。
sleep(),suspend()或resume()都能在不同步的方法内调用,因为它们不需要对锁进行操作。
9.
import java.awt.*;
import java.awt.event.*;
import java.applet.*;
class Blocked extends Thread {
public synchronized void run() {
try {
wait();
}catch(InterruptedException e) {
System.out.println("InterruptedException");
}
System.out.println("Exiting run()");
}
}
public class Interrupt extends Applet {
private Button interrupt = new Button("Interrupt");
private Blocked blocked = new Blocked();
public void init() {//如果首次按下按钮,会看到线程正常退出。但在没有可供杀死的线程以后,看到的便只是按钮按下而已。
add(interrupt);
interrupt.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
System.out.println("Button pressed");
if(blocked == null) return;
Thread remove = blocked;
blocked = null;
remove.interrupt();
}
});
blocked.start();
}
public static void main(String[] args) {
// TODO Auto-generated method stub
Interrupt applet = new Interrupt();
Frame aFrame = new Frame("Interrupt");
aFrame.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
aFrame.add(applet, BorderLayout.CENTER);
aFrame.setSize(350, 550);
applet.init();
applet.start();
aFrame.setVisible(true);
}
}
若标志指出线程应该挂起,便使用wait()命其进入等待状态。若标志指出线程应当恢复,则用一个notify()重新启动线程。
10.
import java.awt.*;
import java.awt.event.*;
import java.applet.*;
public class Suspend extends Applet {
private TextField t = new TextField(10);
private Button suspend = new Button("Suspend"),
resume = new Button("Resume");
class Suspendable extends Thread {
private int count = 0;
private boolean suspend = false;
public Suspendable() {
start();
}
public void fauxSuspend() {
suspend = true;
}
public synchronized void fauxResume() {
suspend = false;
notify();
}
public void run() {
while(true) {
try {
sleep(1000);
synchronized(this) {
while(suspend)
wait();
}
}catch(InterruptedException e) {
}
t.setText(Integer.toString(count++));
}
}
}
private Suspendable ss = new Suspendable();
public void init() {
add(t);
suspend.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
ss.fauxSuspend();
}
});
add(suspend);
resume.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
ss.fauxResume();
}
});
add(resume);
}
public static void main(String[] args) {
// TODO Auto-generated method stub
Suspend applet = new Suspend();
Frame aFrame = new Frame("Suspend");
aFrame.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
aFrame.add(applet, BorderLayout.CENTER);
aFrame.setSize(350, 550);
applet.init();
applet.start();
aFrame.setVisible(true);
}
}
在fauxResume()中,suspend标志被设为false(假),并调用notify()——由于这会在一个"同步"从句中唤醒wait(),所以fauxResume()方法也必须同步,使其能在调用notify()之前取得对象锁(这样一来,对象锁可由要唤醒的那个wait()使用).
11.
在java的Object类型中,都带有一个内存锁,在有线程获取该内存锁后,其他线程无法访问该内存,从而实现java中简单的同步、互斥操作。
synchronized是针对内存区块申请加锁,this关键字代表类的一个对象,所以其内存锁是针对相同对象的互斥操作,而static成员属于类专有,其内存空间为该类所有成员共有,这就导致synchronized()对static成员加锁,相当于对类加锁,也就是在该类的所有成员间实现互斥,在同一时间只有一个线程可访问该类的实例。
Object.wait(),与Object.notify()必须要与synchronized(Obj)一起使用,也就是说,wait与notify是针对已经获取了Obj锁进行操作的,从语法角度来说,Object.wait()和Object.notify()必须在synchronized(this){....}语句块内。从功能上来说wait就是说线程在获取对象锁后,主动释放对象锁,同时本线程休眠。直到有其他线程调用对象的notify()唤醒该线程,才能继续获取对象锁,并继续执行。notify就是对对象锁的唤醒操作。
notify()调用后,并不是马上释放对象锁的,而是在相应的synchronized(){}语句块内执行结束,自动释放锁后,JVM会在wait()对象锁的线程中随即选取一线程,赋予其对象锁,唤醒线程,继续执行。
12.
import java.awt.*;
import java.awt.event.*;
import java.applet.*;
class Ticker2 extends Thread {
private Button b = new Button("Toggle"),
incPriority = new Button("up"),
decPriority = new Button("down");
private TextField t = new TextField(10),
pr = new TextField(3);
private int count = 0;
private boolean runFlag = true;
public Ticker2(Container c) {
b.addActionListener(new ToggleL());
incPriority.addActionListener(new UpL());
decPriority.addActionListener(new DownL());
Panel p = new Panel();
p.add(t);
p.add(pr);
p.add(b);
p.add(incPriority);
p.add(decPriority);
c.add(p);
}
class ToggleL implements ActionListener {
public void actionPerformed(ActionEvent e) {
runFlag = !runFlag;
}
}
class UpL implements ActionListener {
public void actionPerformed(ActionEvent e) {
int newPriority = getPriority() + 1;
if(newPriority > Thread.MAX_PRIORITY)
newPriority = Thread.MAX_PRIORITY;
setPriority(newPriority);
}
}
class DownL implements ActionListener {
public void actionPerformed(ActionEvent e) {
int newPriority = getPriority() - 1;
if(newPriority < Thread.MIN_PRIORITY)
newPriority = Thread.MIN_PRIORITY;
setPriority(newPriority);
}
}
public void run() {
while(true) {
if(runFlag) {
t.setText(Integer.toString(count++));
pr.setText(Integer.toString(getPriority()));
}
yield();//yield()方法只是把线程的状态从执行状态打回就绪状态,所以,执行这个方法,有可能马上又开始运行,有可能等待很长时间。另外线程的控制不是时间的,而是看状态的。
}
}
}
public class Counter5 extends Applet{
private Button start = new Button("Start"),
upMax = new Button("Inc Max Priority"),
downMax = new Button("Dec Max Priority");
private boolean started = false;
public static final int SIZE = 10;
private Ticker2[] s = new Ticker2[SIZE];
private TextField mp = new TextField(3);
public void init() {
for(int i = 0; i < s.length; i++)
s[i] = new Ticker2(this);
add(new Label("MAX_PRIORITY = "+Thread.MAX_PRIORITY));
add(new Label("NIN_PRIORITY = "+Thread.MIN_PRIORITY));
add(new Label("Group Max Priority = "));
add(mp);
add(start);
add(upMax);
add(downMax);
start.addActionListener(new StartL());
upMax.addActionListener(new UpMaxL());
downMax.addActionListener(new DownMaxL());
showMaxPriority();
ThreadGroup parent = s[0].getThreadGroup().getParent();
while(parent != null) {
add(new Label("Parent threadgroup max priority = "+parent.getMaxPriority()));
parent = parent.getParent();
}
}
public void showMaxPriority() {
mp.setText(Integer.toString(s[0].getThreadGroup().getMaxPriority()));
}
class StartL implements ActionListener {
public void actionPerformed(ActionEvent e) {
if(!started) {
started = true;
for(int i = 0; i < s.length; i++)
s[i].start();
}
}
}
class UpMaxL implements ActionListener {
public void actionPerformed(ActionEvent e) {
int maxp = s[0].getThreadGroup().getMaxPriority();
if(++maxp>Thread.MAX_PRIORITY)
maxp = Thread.MAX_PRIORITY;
s[0].getThreadGroup().setMaxPriority(maxp);
showMaxPriority();
}
}
class DownMaxL implements ActionListener {
public void actionPerformed(ActionEvent e) {
int maxp = s[0].getThreadGroup().getMaxPriority();
if(--maxp < Thread.MIN_PRIORITY)
maxp = Thread.MIN_PRIORITY;
s[0].getThreadGroup().setMaxPriority(maxp);
showMaxPriority();
}
}
public static void main(String[] args) {
// TODO Auto-generated method stub
Counter5 applet = new Counter5();
Frame aFrame = new Frame("Counter5");
aFrame.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
aFrame.add(applet, BorderLayout.CENTER);
aFrame.setSize(300, 600);
applet.init();
applet.start();
aFrame.setVisible(true);
}
}
13.
在创建之初,线程被限制到一个组里,而且不能改变到一个不同的组里。每个应用都至少有一个线程从属于系统线程组。若创建多个线程而不指定一个组,它们就会自动归属于系统线程组。线程组也必须从属于其他线程组。必须在构建器里指定新线程组从属于那个线程组。若在创建一个线程组的时候没有指定它的归属,则同样会自动称为系统线程组的一名属下。因此,一个应用程序中的所有线程组最终都将系统线程组作为自己的“父”。
14.
public class ThreadGroup1 {
public static void main(String[] args) {
// TODO Auto-generated method stub
ThreadGroup sys = Thread.currentThread().getThreadGroup();
sys.list();
sys.setMaxPriority(Thread.MAX_PRIORITY-1);
Thread curr = Thread.currentThread();
curr.setPriority(curr.getPriority()+1);
sys.list();
ThreadGroup g1 = new ThreadGroup("g1");
g1.setMaxPriority(Thread.MAX_PRIORITY);//不可能将线程组的最大优先级设为高于它的父线程组
Thread t = new Thread(g1, "A");
t.setPriority(Thread.MAX_PRIORITY);
g1.list();
g1.setMaxPriority(Thread.MAX_PRIORITY-2);
g1.setMaxPriority(Thread.MAX_PRIORITY);//只能降低一个线程组的最大优先级,而不能提高
g1.list();//线程组最大优先级的变化并不能对现有线程造成影响
t = new Thread(g1, "B");
t.setPriority(Thread.MAX_PRIORITY);//新线程不能变到比最大线程组优先级还要高一级
g1.list();
g1.setMaxPriority(Thread.MIN_PRIORITY+2);
t = new Thread(g1, "C");//线程组的最大优先级不会影响默认优先级
g1.list();
t.setPriority(t.getPriority()-1);//只有在试图改变优先级的时候,才会强迫遵守线程组最大优先级的限制。
g1.list();
ThreadGroup g2 = new ThreadGroup(g1, "g2");//g2创建的时候,它会被自动设为g1的线程组最大优先级
g2.list();
g2.setMaxPriority(Thread.MAX_PRIORITY);//g2的优先级无论如何都不可能高于g1
g2.list();
for(int i = 0; i < 5; i++)
new Thread(g2, Integer.toString(i));
sys.list();
System.out.println("Starting all threads");
Thread[] all = new Thread[sys.activeCount()];
sys.enumerate(all);
for(int i = 0; i < all.length; i++)
if(!all[i].isAlive())
all[i].start();
System.out.println("All threads started");
sys.suspend();
System.out.println("All threads suspened");
sys.stop();
System.out.println("All threads stoped");
}
}
一个子组的最大优先级在任何时候都只能低于或等于它的父组的最大优先级。